16d528ed9Sopenharmony_ci// Copyright 2014 The Chromium Authors. All rights reserved.
26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
36d528ed9Sopenharmony_ci// found in the LICENSE file.
46d528ed9Sopenharmony_ci
56d528ed9Sopenharmony_ci#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
66d528ed9Sopenharmony_ci#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
76d528ed9Sopenharmony_ci
86d528ed9Sopenharmony_ci#include <stdint.h>
96d528ed9Sopenharmony_ci
106d528ed9Sopenharmony_ci#include <limits>
116d528ed9Sopenharmony_ci#include <type_traits>
126d528ed9Sopenharmony_ci
136d528ed9Sopenharmony_ci#if defined(__GNUC__) || defined(__clang__)
146d528ed9Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
156d528ed9Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
166d528ed9Sopenharmony_ci#else
176d528ed9Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) (x)
186d528ed9Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) (x)
196d528ed9Sopenharmony_ci#endif
206d528ed9Sopenharmony_ci
216d528ed9Sopenharmony_cinamespace base {
226d528ed9Sopenharmony_cinamespace internal {
236d528ed9Sopenharmony_ci
246d528ed9Sopenharmony_ci// The std library doesn't provide a binary max_exponent for integers, however
256d528ed9Sopenharmony_ci// we can compute an analog using std::numeric_limits<>::digits.
266d528ed9Sopenharmony_citemplate <typename NumericType>
276d528ed9Sopenharmony_cistruct MaxExponent {
286d528ed9Sopenharmony_ci  static const int value = std::is_floating_point<NumericType>::value
296d528ed9Sopenharmony_ci                               ? std::numeric_limits<NumericType>::max_exponent
306d528ed9Sopenharmony_ci                               : std::numeric_limits<NumericType>::digits + 1;
316d528ed9Sopenharmony_ci};
326d528ed9Sopenharmony_ci
336d528ed9Sopenharmony_ci// The number of bits (including the sign) in an integer. Eliminates sizeof
346d528ed9Sopenharmony_ci// hacks.
356d528ed9Sopenharmony_citemplate <typename NumericType>
366d528ed9Sopenharmony_cistruct IntegerBitsPlusSign {
376d528ed9Sopenharmony_ci  static const int value = std::numeric_limits<NumericType>::digits +
386d528ed9Sopenharmony_ci                           std::is_signed<NumericType>::value;
396d528ed9Sopenharmony_ci};
406d528ed9Sopenharmony_ci
416d528ed9Sopenharmony_ci// Helper templates for integer manipulations.
426d528ed9Sopenharmony_ci
436d528ed9Sopenharmony_citemplate <typename Integer>
446d528ed9Sopenharmony_cistruct PositionOfSignBit {
456d528ed9Sopenharmony_ci  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
466d528ed9Sopenharmony_ci};
476d528ed9Sopenharmony_ci
486d528ed9Sopenharmony_ci// Determines if a numeric value is negative without throwing compiler
496d528ed9Sopenharmony_ci// warnings on: unsigned(value) < 0.
506d528ed9Sopenharmony_citemplate <typename T,
516d528ed9Sopenharmony_ci          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
526d528ed9Sopenharmony_ciconstexpr bool IsValueNegative(T value) {
536d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
546d528ed9Sopenharmony_ci  return value < 0;
556d528ed9Sopenharmony_ci}
566d528ed9Sopenharmony_ci
576d528ed9Sopenharmony_citemplate <typename T,
586d528ed9Sopenharmony_ci          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
596d528ed9Sopenharmony_ciconstexpr bool IsValueNegative(T) {
606d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
616d528ed9Sopenharmony_ci  return false;
626d528ed9Sopenharmony_ci}
636d528ed9Sopenharmony_ci
646d528ed9Sopenharmony_ci// This performs a fast negation, returning a signed value. It works on unsigned
656d528ed9Sopenharmony_ci// arguments, but probably doesn't do what you want for any unsigned value
666d528ed9Sopenharmony_ci// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
676d528ed9Sopenharmony_citemplate <typename T>
686d528ed9Sopenharmony_ciconstexpr typename std::make_signed<T>::type ConditionalNegate(
696d528ed9Sopenharmony_ci    T x,
706d528ed9Sopenharmony_ci    bool is_negative) {
716d528ed9Sopenharmony_ci  static_assert(std::is_integral<T>::value, "Type must be integral");
726d528ed9Sopenharmony_ci  using SignedT = typename std::make_signed<T>::type;
736d528ed9Sopenharmony_ci  using UnsignedT = typename std::make_unsigned<T>::type;
746d528ed9Sopenharmony_ci  return static_cast<SignedT>(
756d528ed9Sopenharmony_ci      (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
766d528ed9Sopenharmony_ci}
776d528ed9Sopenharmony_ci
786d528ed9Sopenharmony_ci// This performs a safe, absolute value via unsigned overflow.
796d528ed9Sopenharmony_citemplate <typename T>
806d528ed9Sopenharmony_ciconstexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
816d528ed9Sopenharmony_ci  static_assert(std::is_integral<T>::value, "Type must be integral");
826d528ed9Sopenharmony_ci  using UnsignedT = typename std::make_unsigned<T>::type;
836d528ed9Sopenharmony_ci  return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
846d528ed9Sopenharmony_ci                                : static_cast<UnsignedT>(value);
856d528ed9Sopenharmony_ci}
866d528ed9Sopenharmony_ci
876d528ed9Sopenharmony_ci// This allows us to switch paths on known compile-time constants.
886d528ed9Sopenharmony_ci#if defined(__clang__) || defined(__GNUC__)
896d528ed9Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() {
906d528ed9Sopenharmony_ci  return true;
916d528ed9Sopenharmony_ci}
926d528ed9Sopenharmony_citemplate <typename T>
936d528ed9Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T v) {
946d528ed9Sopenharmony_ci  return __builtin_constant_p(v);
956d528ed9Sopenharmony_ci}
966d528ed9Sopenharmony_ci#else
976d528ed9Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() {
986d528ed9Sopenharmony_ci  return false;
996d528ed9Sopenharmony_ci}
1006d528ed9Sopenharmony_citemplate <typename T>
1016d528ed9Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T) {
1026d528ed9Sopenharmony_ci  return false;
1036d528ed9Sopenharmony_ci}
1046d528ed9Sopenharmony_ci#endif
1056d528ed9Sopenharmony_citemplate <typename T>
1066d528ed9Sopenharmony_ciconstexpr bool MustTreatAsConstexpr(const T v) {
1076d528ed9Sopenharmony_ci  // Either we can't detect a compile-time constant, and must always use the
1086d528ed9Sopenharmony_ci  // constexpr path, or we know we have a compile-time constant.
1096d528ed9Sopenharmony_ci  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
1106d528ed9Sopenharmony_ci}
1116d528ed9Sopenharmony_ci
1126d528ed9Sopenharmony_ci// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
1136d528ed9Sopenharmony_ci// Also used in a constexpr template to trigger a compilation failure on
1146d528ed9Sopenharmony_ci// an error condition.
1156d528ed9Sopenharmony_cistruct CheckOnFailure {
1166d528ed9Sopenharmony_ci  template <typename T>
1176d528ed9Sopenharmony_ci  static T HandleFailure() {
1186d528ed9Sopenharmony_ci#if defined(_MSC_VER)
1196d528ed9Sopenharmony_ci    __debugbreak();
1206d528ed9Sopenharmony_ci#elif defined(__GNUC__) || defined(__clang__)
1216d528ed9Sopenharmony_ci    __builtin_trap();
1226d528ed9Sopenharmony_ci#else
1236d528ed9Sopenharmony_ci    ((void)(*(volatile char*)0 = 0));
1246d528ed9Sopenharmony_ci#endif
1256d528ed9Sopenharmony_ci    return T();
1266d528ed9Sopenharmony_ci  }
1276d528ed9Sopenharmony_ci};
1286d528ed9Sopenharmony_ci
1296d528ed9Sopenharmony_cienum IntegerRepresentation {
1306d528ed9Sopenharmony_ci  INTEGER_REPRESENTATION_UNSIGNED,
1316d528ed9Sopenharmony_ci  INTEGER_REPRESENTATION_SIGNED
1326d528ed9Sopenharmony_ci};
1336d528ed9Sopenharmony_ci
1346d528ed9Sopenharmony_ci// A range for a given nunmeric Src type is contained for a given numeric Dst
1356d528ed9Sopenharmony_ci// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
1366d528ed9Sopenharmony_ci// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
1376d528ed9Sopenharmony_ci// We implement this as template specializations rather than simple static
1386d528ed9Sopenharmony_ci// comparisons to ensure type correctness in our comparisons.
1396d528ed9Sopenharmony_cienum NumericRangeRepresentation {
1406d528ed9Sopenharmony_ci  NUMERIC_RANGE_NOT_CONTAINED,
1416d528ed9Sopenharmony_ci  NUMERIC_RANGE_CONTAINED
1426d528ed9Sopenharmony_ci};
1436d528ed9Sopenharmony_ci
1446d528ed9Sopenharmony_ci// Helper templates to statically determine if our destination type can contain
1456d528ed9Sopenharmony_ci// maximum and minimum values represented by the source type.
1466d528ed9Sopenharmony_ci
1476d528ed9Sopenharmony_citemplate <typename Dst,
1486d528ed9Sopenharmony_ci          typename Src,
1496d528ed9Sopenharmony_ci          IntegerRepresentation DstSign = std::is_signed<Dst>::value
1506d528ed9Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
1516d528ed9Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
1526d528ed9Sopenharmony_ci          IntegerRepresentation SrcSign = std::is_signed<Src>::value
1536d528ed9Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
1546d528ed9Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED>
1556d528ed9Sopenharmony_cistruct StaticDstRangeRelationToSrcRange;
1566d528ed9Sopenharmony_ci
1576d528ed9Sopenharmony_ci// Same sign: Dst is guaranteed to contain Src only if its range is equal or
1586d528ed9Sopenharmony_ci// larger.
1596d528ed9Sopenharmony_citemplate <typename Dst, typename Src, IntegerRepresentation Sign>
1606d528ed9Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
1616d528ed9Sopenharmony_ci  static const NumericRangeRepresentation value =
1626d528ed9Sopenharmony_ci      MaxExponent<Dst>::value >= MaxExponent<Src>::value
1636d528ed9Sopenharmony_ci          ? NUMERIC_RANGE_CONTAINED
1646d528ed9Sopenharmony_ci          : NUMERIC_RANGE_NOT_CONTAINED;
1656d528ed9Sopenharmony_ci};
1666d528ed9Sopenharmony_ci
1676d528ed9Sopenharmony_ci// Unsigned to signed: Dst is guaranteed to contain source only if its range is
1686d528ed9Sopenharmony_ci// larger.
1696d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
1706d528ed9Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst,
1716d528ed9Sopenharmony_ci                                        Src,
1726d528ed9Sopenharmony_ci                                        INTEGER_REPRESENTATION_SIGNED,
1736d528ed9Sopenharmony_ci                                        INTEGER_REPRESENTATION_UNSIGNED> {
1746d528ed9Sopenharmony_ci  static const NumericRangeRepresentation value =
1756d528ed9Sopenharmony_ci      MaxExponent<Dst>::value > MaxExponent<Src>::value
1766d528ed9Sopenharmony_ci          ? NUMERIC_RANGE_CONTAINED
1776d528ed9Sopenharmony_ci          : NUMERIC_RANGE_NOT_CONTAINED;
1786d528ed9Sopenharmony_ci};
1796d528ed9Sopenharmony_ci
1806d528ed9Sopenharmony_ci// Signed to unsigned: Dst cannot be statically determined to contain Src.
1816d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
1826d528ed9Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst,
1836d528ed9Sopenharmony_ci                                        Src,
1846d528ed9Sopenharmony_ci                                        INTEGER_REPRESENTATION_UNSIGNED,
1856d528ed9Sopenharmony_ci                                        INTEGER_REPRESENTATION_SIGNED> {
1866d528ed9Sopenharmony_ci  static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
1876d528ed9Sopenharmony_ci};
1886d528ed9Sopenharmony_ci
1896d528ed9Sopenharmony_ci// This class wraps the range constraints as separate booleans so the compiler
1906d528ed9Sopenharmony_ci// can identify constants and eliminate unused code paths.
1916d528ed9Sopenharmony_ciclass RangeCheck {
1926d528ed9Sopenharmony_ci public:
1936d528ed9Sopenharmony_ci  constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
1946d528ed9Sopenharmony_ci      : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
1956d528ed9Sopenharmony_ci  constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
1966d528ed9Sopenharmony_ci  constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
1976d528ed9Sopenharmony_ci  constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
1986d528ed9Sopenharmony_ci  constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
1996d528ed9Sopenharmony_ci  constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
2006d528ed9Sopenharmony_ci  constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
2016d528ed9Sopenharmony_ci  constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
2026d528ed9Sopenharmony_ci  constexpr bool operator==(const RangeCheck rhs) const {
2036d528ed9Sopenharmony_ci    return is_underflow_ == rhs.is_underflow_ &&
2046d528ed9Sopenharmony_ci           is_overflow_ == rhs.is_overflow_;
2056d528ed9Sopenharmony_ci  }
2066d528ed9Sopenharmony_ci  constexpr bool operator!=(const RangeCheck rhs) const {
2076d528ed9Sopenharmony_ci    return !(*this == rhs);
2086d528ed9Sopenharmony_ci  }
2096d528ed9Sopenharmony_ci
2106d528ed9Sopenharmony_ci private:
2116d528ed9Sopenharmony_ci  // Do not change the order of these member variables. The integral conversion
2126d528ed9Sopenharmony_ci  // optimization depends on this exact order.
2136d528ed9Sopenharmony_ci  const bool is_underflow_;
2146d528ed9Sopenharmony_ci  const bool is_overflow_;
2156d528ed9Sopenharmony_ci};
2166d528ed9Sopenharmony_ci
2176d528ed9Sopenharmony_ci// The following helper template addresses a corner case in range checks for
2186d528ed9Sopenharmony_ci// conversion from a floating-point type to an integral type of smaller range
2196d528ed9Sopenharmony_ci// but larger precision (e.g. float -> unsigned). The problem is as follows:
2206d528ed9Sopenharmony_ci//   1. Integral maximum is always one less than a power of two, so it must be
2216d528ed9Sopenharmony_ci//      truncated to fit the mantissa of the floating point. The direction of
2226d528ed9Sopenharmony_ci//      rounding is implementation defined, but by default it's always IEEE
2236d528ed9Sopenharmony_ci//      floats, which round to nearest and thus result in a value of larger
2246d528ed9Sopenharmony_ci//      magnitude than the integral value.
2256d528ed9Sopenharmony_ci//      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
2266d528ed9Sopenharmony_ci//                                   // is 4294967295u.
2276d528ed9Sopenharmony_ci//   2. If the floating point value is equal to the promoted integral maximum
2286d528ed9Sopenharmony_ci//      value, a range check will erroneously pass.
2296d528ed9Sopenharmony_ci//      Example: (4294967296f <= 4294967295u) // This is true due to a precision
2306d528ed9Sopenharmony_ci//                                            // loss in rounding up to float.
2316d528ed9Sopenharmony_ci//   3. When the floating point value is then converted to an integral, the
2326d528ed9Sopenharmony_ci//      resulting value is out of range for the target integral type and
2336d528ed9Sopenharmony_ci//      thus is implementation defined.
2346d528ed9Sopenharmony_ci//      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
2356d528ed9Sopenharmony_ci// To fix this bug we manually truncate the maximum value when the destination
2366d528ed9Sopenharmony_ci// type is an integral of larger precision than the source floating-point type,
2376d528ed9Sopenharmony_ci// such that the resulting maximum is represented exactly as a floating point.
2386d528ed9Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
2396d528ed9Sopenharmony_cistruct NarrowingRange {
2406d528ed9Sopenharmony_ci  using SrcLimits = std::numeric_limits<Src>;
2416d528ed9Sopenharmony_ci  using DstLimits = typename std::numeric_limits<Dst>;
2426d528ed9Sopenharmony_ci
2436d528ed9Sopenharmony_ci  // Computes the mask required to make an accurate comparison between types.
2446d528ed9Sopenharmony_ci  static const int kShift =
2456d528ed9Sopenharmony_ci      (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
2466d528ed9Sopenharmony_ci       SrcLimits::digits < DstLimits::digits)
2476d528ed9Sopenharmony_ci          ? (DstLimits::digits - SrcLimits::digits)
2486d528ed9Sopenharmony_ci          : 0;
2496d528ed9Sopenharmony_ci  template <
2506d528ed9Sopenharmony_ci      typename T,
2516d528ed9Sopenharmony_ci      typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
2526d528ed9Sopenharmony_ci
2536d528ed9Sopenharmony_ci  // Masks out the integer bits that are beyond the precision of the
2546d528ed9Sopenharmony_ci  // intermediate type used for comparison.
2556d528ed9Sopenharmony_ci  static constexpr T Adjust(T value) {
2566d528ed9Sopenharmony_ci    static_assert(std::is_same<T, Dst>::value, "");
2576d528ed9Sopenharmony_ci    static_assert(kShift < DstLimits::digits, "");
2586d528ed9Sopenharmony_ci    return static_cast<T>(
2596d528ed9Sopenharmony_ci        ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
2606d528ed9Sopenharmony_ci                          IsValueNegative(value)));
2616d528ed9Sopenharmony_ci  }
2626d528ed9Sopenharmony_ci
2636d528ed9Sopenharmony_ci  template <typename T,
2646d528ed9Sopenharmony_ci            typename std::enable_if<std::is_floating_point<T>::value>::type* =
2656d528ed9Sopenharmony_ci                nullptr>
2666d528ed9Sopenharmony_ci  static constexpr T Adjust(T value) {
2676d528ed9Sopenharmony_ci    static_assert(std::is_same<T, Dst>::value, "");
2686d528ed9Sopenharmony_ci    static_assert(kShift == 0, "");
2696d528ed9Sopenharmony_ci    return value;
2706d528ed9Sopenharmony_ci  }
2716d528ed9Sopenharmony_ci
2726d528ed9Sopenharmony_ci  static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
2736d528ed9Sopenharmony_ci  static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
2746d528ed9Sopenharmony_ci};
2756d528ed9Sopenharmony_ci
2766d528ed9Sopenharmony_citemplate <typename Dst,
2776d528ed9Sopenharmony_ci          typename Src,
2786d528ed9Sopenharmony_ci          template <typename>
2796d528ed9Sopenharmony_ci          class Bounds,
2806d528ed9Sopenharmony_ci          IntegerRepresentation DstSign = std::is_signed<Dst>::value
2816d528ed9Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
2826d528ed9Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
2836d528ed9Sopenharmony_ci          IntegerRepresentation SrcSign = std::is_signed<Src>::value
2846d528ed9Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
2856d528ed9Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
2866d528ed9Sopenharmony_ci          NumericRangeRepresentation DstRange =
2876d528ed9Sopenharmony_ci              StaticDstRangeRelationToSrcRange<Dst, Src>::value>
2886d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl;
2896d528ed9Sopenharmony_ci
2906d528ed9Sopenharmony_ci// The following templates are for ranges that must be verified at runtime. We
2916d528ed9Sopenharmony_ci// split it into checks based on signedness to avoid confusing casts and
2926d528ed9Sopenharmony_ci// compiler warnings on signed an unsigned comparisons.
2936d528ed9Sopenharmony_ci
2946d528ed9Sopenharmony_ci// Same sign narrowing: The range is contained for normal limits.
2956d528ed9Sopenharmony_citemplate <typename Dst,
2966d528ed9Sopenharmony_ci          typename Src,
2976d528ed9Sopenharmony_ci          template <typename>
2986d528ed9Sopenharmony_ci          class Bounds,
2996d528ed9Sopenharmony_ci          IntegerRepresentation DstSign,
3006d528ed9Sopenharmony_ci          IntegerRepresentation SrcSign>
3016d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst,
3026d528ed9Sopenharmony_ci                                      Src,
3036d528ed9Sopenharmony_ci                                      Bounds,
3046d528ed9Sopenharmony_ci                                      DstSign,
3056d528ed9Sopenharmony_ci                                      SrcSign,
3066d528ed9Sopenharmony_ci                                      NUMERIC_RANGE_CONTAINED> {
3076d528ed9Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3086d528ed9Sopenharmony_ci    using SrcLimits = std::numeric_limits<Src>;
3096d528ed9Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3106d528ed9Sopenharmony_ci    return RangeCheck(
3116d528ed9Sopenharmony_ci        static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
3126d528ed9Sopenharmony_ci            static_cast<Dst>(value) >= DstLimits::lowest(),
3136d528ed9Sopenharmony_ci        static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
3146d528ed9Sopenharmony_ci            static_cast<Dst>(value) <= DstLimits::max());
3156d528ed9Sopenharmony_ci  }
3166d528ed9Sopenharmony_ci};
3176d528ed9Sopenharmony_ci
3186d528ed9Sopenharmony_ci// Signed to signed narrowing: Both the upper and lower boundaries may be
3196d528ed9Sopenharmony_ci// exceeded for standard limits.
3206d528ed9Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3216d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst,
3226d528ed9Sopenharmony_ci                                      Src,
3236d528ed9Sopenharmony_ci                                      Bounds,
3246d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_SIGNED,
3256d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_SIGNED,
3266d528ed9Sopenharmony_ci                                      NUMERIC_RANGE_NOT_CONTAINED> {
3276d528ed9Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3286d528ed9Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3296d528ed9Sopenharmony_ci    return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
3306d528ed9Sopenharmony_ci  }
3316d528ed9Sopenharmony_ci};
3326d528ed9Sopenharmony_ci
3336d528ed9Sopenharmony_ci// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
3346d528ed9Sopenharmony_ci// standard limits.
3356d528ed9Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3366d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst,
3376d528ed9Sopenharmony_ci                                      Src,
3386d528ed9Sopenharmony_ci                                      Bounds,
3396d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_UNSIGNED,
3406d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_UNSIGNED,
3416d528ed9Sopenharmony_ci                                      NUMERIC_RANGE_NOT_CONTAINED> {
3426d528ed9Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3436d528ed9Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3446d528ed9Sopenharmony_ci    return RangeCheck(
3456d528ed9Sopenharmony_ci        DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
3466d528ed9Sopenharmony_ci        value <= DstLimits::max());
3476d528ed9Sopenharmony_ci  }
3486d528ed9Sopenharmony_ci};
3496d528ed9Sopenharmony_ci
3506d528ed9Sopenharmony_ci// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
3516d528ed9Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3526d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst,
3536d528ed9Sopenharmony_ci                                      Src,
3546d528ed9Sopenharmony_ci                                      Bounds,
3556d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_SIGNED,
3566d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_UNSIGNED,
3576d528ed9Sopenharmony_ci                                      NUMERIC_RANGE_NOT_CONTAINED> {
3586d528ed9Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3596d528ed9Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3606d528ed9Sopenharmony_ci    using Promotion = decltype(Src() + Dst());
3616d528ed9Sopenharmony_ci    return RangeCheck(DstLimits::lowest() <= Dst(0) ||
3626d528ed9Sopenharmony_ci                          static_cast<Promotion>(value) >=
3636d528ed9Sopenharmony_ci                              static_cast<Promotion>(DstLimits::lowest()),
3646d528ed9Sopenharmony_ci                      static_cast<Promotion>(value) <=
3656d528ed9Sopenharmony_ci                          static_cast<Promotion>(DstLimits::max()));
3666d528ed9Sopenharmony_ci  }
3676d528ed9Sopenharmony_ci};
3686d528ed9Sopenharmony_ci
3696d528ed9Sopenharmony_ci// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
3706d528ed9Sopenharmony_ci// and any negative value exceeds the lower boundary for standard limits.
3716d528ed9Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3726d528ed9Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst,
3736d528ed9Sopenharmony_ci                                      Src,
3746d528ed9Sopenharmony_ci                                      Bounds,
3756d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_UNSIGNED,
3766d528ed9Sopenharmony_ci                                      INTEGER_REPRESENTATION_SIGNED,
3776d528ed9Sopenharmony_ci                                      NUMERIC_RANGE_NOT_CONTAINED> {
3786d528ed9Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3796d528ed9Sopenharmony_ci    using SrcLimits = std::numeric_limits<Src>;
3806d528ed9Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3816d528ed9Sopenharmony_ci    using Promotion = decltype(Src() + Dst());
3826d528ed9Sopenharmony_ci    return RangeCheck(
3836d528ed9Sopenharmony_ci        value >= Src(0) && (DstLimits::lowest() == 0 ||
3846d528ed9Sopenharmony_ci                            static_cast<Dst>(value) >= DstLimits::lowest()),
3856d528ed9Sopenharmony_ci        static_cast<Promotion>(SrcLimits::max()) <=
3866d528ed9Sopenharmony_ci                static_cast<Promotion>(DstLimits::max()) ||
3876d528ed9Sopenharmony_ci            static_cast<Promotion>(value) <=
3886d528ed9Sopenharmony_ci                static_cast<Promotion>(DstLimits::max()));
3896d528ed9Sopenharmony_ci  }
3906d528ed9Sopenharmony_ci};
3916d528ed9Sopenharmony_ci
3926d528ed9Sopenharmony_ci// Simple wrapper for statically checking if a type's range is contained.
3936d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
3946d528ed9Sopenharmony_cistruct IsTypeInRangeForNumericType {
3956d528ed9Sopenharmony_ci  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
3966d528ed9Sopenharmony_ci                            NUMERIC_RANGE_CONTAINED;
3976d528ed9Sopenharmony_ci};
3986d528ed9Sopenharmony_ci
3996d528ed9Sopenharmony_citemplate <typename Dst,
4006d528ed9Sopenharmony_ci          template <typename> class Bounds = std::numeric_limits,
4016d528ed9Sopenharmony_ci          typename Src>
4026d528ed9Sopenharmony_ciconstexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
4036d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
4046d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
4056d528ed9Sopenharmony_ci  static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
4066d528ed9Sopenharmony_ci  return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
4076d528ed9Sopenharmony_ci}
4086d528ed9Sopenharmony_ci
4096d528ed9Sopenharmony_ci// Integer promotion templates used by the portable checked integer arithmetic.
4106d528ed9Sopenharmony_citemplate <size_t Size, bool IsSigned>
4116d528ed9Sopenharmony_cistruct IntegerForDigitsAndSign;
4126d528ed9Sopenharmony_ci
4136d528ed9Sopenharmony_ci#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
4146d528ed9Sopenharmony_ci  template <>                                                   \
4156d528ed9Sopenharmony_ci  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
4166d528ed9Sopenharmony_ci                                 std::is_signed<I>::value> {    \
4176d528ed9Sopenharmony_ci    using type = I;                                             \
4186d528ed9Sopenharmony_ci  }
4196d528ed9Sopenharmony_ci
4206d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int8_t);
4216d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
4226d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int16_t);
4236d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
4246d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int32_t);
4256d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
4266d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int64_t);
4276d528ed9Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
4286d528ed9Sopenharmony_ci#undef INTEGER_FOR_DIGITS_AND_SIGN
4296d528ed9Sopenharmony_ci
4306d528ed9Sopenharmony_ci// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
4316d528ed9Sopenharmony_ci// support 128-bit math, then the ArithmeticPromotion template below will need
4326d528ed9Sopenharmony_ci// to be updated (or more likely replaced with a decltype expression).
4336d528ed9Sopenharmony_cistatic_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
4346d528ed9Sopenharmony_ci              "Max integer size not supported for this toolchain.");
4356d528ed9Sopenharmony_ci
4366d528ed9Sopenharmony_citemplate <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
4376d528ed9Sopenharmony_cistruct TwiceWiderInteger {
4386d528ed9Sopenharmony_ci  using type =
4396d528ed9Sopenharmony_ci      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
4406d528ed9Sopenharmony_ci                                       IsSigned>::type;
4416d528ed9Sopenharmony_ci};
4426d528ed9Sopenharmony_ci
4436d528ed9Sopenharmony_cienum ArithmeticPromotionCategory {
4446d528ed9Sopenharmony_ci  LEFT_PROMOTION,  // Use the type of the left-hand argument.
4456d528ed9Sopenharmony_ci  RIGHT_PROMOTION  // Use the type of the right-hand argument.
4466d528ed9Sopenharmony_ci};
4476d528ed9Sopenharmony_ci
4486d528ed9Sopenharmony_ci// Determines the type that can represent the largest positive value.
4496d528ed9Sopenharmony_citemplate <typename Lhs,
4506d528ed9Sopenharmony_ci          typename Rhs,
4516d528ed9Sopenharmony_ci          ArithmeticPromotionCategory Promotion =
4526d528ed9Sopenharmony_ci              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
4536d528ed9Sopenharmony_ci                  ? LEFT_PROMOTION
4546d528ed9Sopenharmony_ci                  : RIGHT_PROMOTION>
4556d528ed9Sopenharmony_cistruct MaxExponentPromotion;
4566d528ed9Sopenharmony_ci
4576d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
4586d528ed9Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
4596d528ed9Sopenharmony_ci  using type = Lhs;
4606d528ed9Sopenharmony_ci};
4616d528ed9Sopenharmony_ci
4626d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
4636d528ed9Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
4646d528ed9Sopenharmony_ci  using type = Rhs;
4656d528ed9Sopenharmony_ci};
4666d528ed9Sopenharmony_ci
4676d528ed9Sopenharmony_ci// Determines the type that can represent the lowest arithmetic value.
4686d528ed9Sopenharmony_citemplate <typename Lhs,
4696d528ed9Sopenharmony_ci          typename Rhs,
4706d528ed9Sopenharmony_ci          ArithmeticPromotionCategory Promotion =
4716d528ed9Sopenharmony_ci              std::is_signed<Lhs>::value
4726d528ed9Sopenharmony_ci                  ? (std::is_signed<Rhs>::value
4736d528ed9Sopenharmony_ci                         ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
4746d528ed9Sopenharmony_ci                                ? LEFT_PROMOTION
4756d528ed9Sopenharmony_ci                                : RIGHT_PROMOTION)
4766d528ed9Sopenharmony_ci                         : LEFT_PROMOTION)
4776d528ed9Sopenharmony_ci                  : (std::is_signed<Rhs>::value
4786d528ed9Sopenharmony_ci                         ? RIGHT_PROMOTION
4796d528ed9Sopenharmony_ci                         : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
4806d528ed9Sopenharmony_ci                                ? LEFT_PROMOTION
4816d528ed9Sopenharmony_ci                                : RIGHT_PROMOTION))>
4826d528ed9Sopenharmony_cistruct LowestValuePromotion;
4836d528ed9Sopenharmony_ci
4846d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
4856d528ed9Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
4866d528ed9Sopenharmony_ci  using type = Lhs;
4876d528ed9Sopenharmony_ci};
4886d528ed9Sopenharmony_ci
4896d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
4906d528ed9Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
4916d528ed9Sopenharmony_ci  using type = Rhs;
4926d528ed9Sopenharmony_ci};
4936d528ed9Sopenharmony_ci
4946d528ed9Sopenharmony_ci// Determines the type that is best able to represent an arithmetic result.
4956d528ed9Sopenharmony_citemplate <
4966d528ed9Sopenharmony_ci    typename Lhs,
4976d528ed9Sopenharmony_ci    typename Rhs = Lhs,
4986d528ed9Sopenharmony_ci    bool is_intmax_type =
4996d528ed9Sopenharmony_ci        std::is_integral<
5006d528ed9Sopenharmony_ci            typename MaxExponentPromotion<Lhs, Rhs>::type>::value &&
5016d528ed9Sopenharmony_ci        IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
5026d528ed9Sopenharmony_ci                value == IntegerBitsPlusSign<intmax_t>::value,
5036d528ed9Sopenharmony_ci    bool is_max_exponent = StaticDstRangeRelationToSrcRange<
5046d528ed9Sopenharmony_ci                               typename MaxExponentPromotion<Lhs, Rhs>::type,
5056d528ed9Sopenharmony_ci                               Lhs>::value == NUMERIC_RANGE_CONTAINED &&
5066d528ed9Sopenharmony_ci                           StaticDstRangeRelationToSrcRange<
5076d528ed9Sopenharmony_ci                               typename MaxExponentPromotion<Lhs, Rhs>::type,
5086d528ed9Sopenharmony_ci                               Rhs>::value == NUMERIC_RANGE_CONTAINED>
5096d528ed9Sopenharmony_cistruct BigEnoughPromotion;
5106d528ed9Sopenharmony_ci
5116d528ed9Sopenharmony_ci// The side with the max exponent is big enough.
5126d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs, bool is_intmax_type>
5136d528ed9Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
5146d528ed9Sopenharmony_ci  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
5156d528ed9Sopenharmony_ci  static const bool is_contained = true;
5166d528ed9Sopenharmony_ci};
5176d528ed9Sopenharmony_ci
5186d528ed9Sopenharmony_ci// We can use a twice wider type to fit.
5196d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
5206d528ed9Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, false, false> {
5216d528ed9Sopenharmony_ci  using type =
5226d528ed9Sopenharmony_ci      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
5236d528ed9Sopenharmony_ci                                 std::is_signed<Lhs>::value ||
5246d528ed9Sopenharmony_ci                                     std::is_signed<Rhs>::value>::type;
5256d528ed9Sopenharmony_ci  static const bool is_contained = true;
5266d528ed9Sopenharmony_ci};
5276d528ed9Sopenharmony_ci
5286d528ed9Sopenharmony_ci// No type is large enough.
5296d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
5306d528ed9Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, true, false> {
5316d528ed9Sopenharmony_ci  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
5326d528ed9Sopenharmony_ci  static const bool is_contained = false;
5336d528ed9Sopenharmony_ci};
5346d528ed9Sopenharmony_ci
5356d528ed9Sopenharmony_ci// We can statically check if operations on the provided types can wrap, so we
5366d528ed9Sopenharmony_ci// can skip the checked operations if they're not needed. So, for an integer we
5376d528ed9Sopenharmony_ci// care if the destination type preserves the sign and is twice the width of
5386d528ed9Sopenharmony_ci// the source.
5396d528ed9Sopenharmony_citemplate <typename T, typename Lhs, typename Rhs = Lhs>
5406d528ed9Sopenharmony_cistruct IsIntegerArithmeticSafe {
5416d528ed9Sopenharmony_ci  static const bool value =
5426d528ed9Sopenharmony_ci      !std::is_floating_point<T>::value &&
5436d528ed9Sopenharmony_ci      !std::is_floating_point<Lhs>::value &&
5446d528ed9Sopenharmony_ci      !std::is_floating_point<Rhs>::value &&
5456d528ed9Sopenharmony_ci      std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
5466d528ed9Sopenharmony_ci      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
5476d528ed9Sopenharmony_ci      std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
5486d528ed9Sopenharmony_ci      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
5496d528ed9Sopenharmony_ci};
5506d528ed9Sopenharmony_ci
5516d528ed9Sopenharmony_ci// Promotes to a type that can represent any possible result of a binary
5526d528ed9Sopenharmony_ci// arithmetic operation with the source types.
5536d528ed9Sopenharmony_citemplate <typename Lhs,
5546d528ed9Sopenharmony_ci          typename Rhs,
5556d528ed9Sopenharmony_ci          bool is_promotion_possible = IsIntegerArithmeticSafe<
5566d528ed9Sopenharmony_ci              typename std::conditional<std::is_signed<Lhs>::value ||
5576d528ed9Sopenharmony_ci                                            std::is_signed<Rhs>::value,
5586d528ed9Sopenharmony_ci                                        intmax_t,
5596d528ed9Sopenharmony_ci                                        uintmax_t>::type,
5606d528ed9Sopenharmony_ci              typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
5616d528ed9Sopenharmony_cistruct FastIntegerArithmeticPromotion;
5626d528ed9Sopenharmony_ci
5636d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
5646d528ed9Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
5656d528ed9Sopenharmony_ci  using type =
5666d528ed9Sopenharmony_ci      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
5676d528ed9Sopenharmony_ci                                 std::is_signed<Lhs>::value ||
5686d528ed9Sopenharmony_ci                                     std::is_signed<Rhs>::value>::type;
5696d528ed9Sopenharmony_ci  static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
5706d528ed9Sopenharmony_ci  static const bool is_contained = true;
5716d528ed9Sopenharmony_ci};
5726d528ed9Sopenharmony_ci
5736d528ed9Sopenharmony_citemplate <typename Lhs, typename Rhs>
5746d528ed9Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
5756d528ed9Sopenharmony_ci  using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
5766d528ed9Sopenharmony_ci  static const bool is_contained = false;
5776d528ed9Sopenharmony_ci};
5786d528ed9Sopenharmony_ci
5796d528ed9Sopenharmony_ci// Extracts the underlying type from an enum.
5806d528ed9Sopenharmony_citemplate <typename T, bool is_enum = std::is_enum<T>::value>
5816d528ed9Sopenharmony_cistruct ArithmeticOrUnderlyingEnum;
5826d528ed9Sopenharmony_ci
5836d528ed9Sopenharmony_citemplate <typename T>
5846d528ed9Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, true> {
5856d528ed9Sopenharmony_ci  using type = typename std::underlying_type<T>::type;
5866d528ed9Sopenharmony_ci  static const bool value = std::is_arithmetic<type>::value;
5876d528ed9Sopenharmony_ci};
5886d528ed9Sopenharmony_ci
5896d528ed9Sopenharmony_citemplate <typename T>
5906d528ed9Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, false> {
5916d528ed9Sopenharmony_ci  using type = T;
5926d528ed9Sopenharmony_ci  static const bool value = std::is_arithmetic<type>::value;
5936d528ed9Sopenharmony_ci};
5946d528ed9Sopenharmony_ci
5956d528ed9Sopenharmony_ci// The following are helper templates used in the CheckedNumeric class.
5966d528ed9Sopenharmony_citemplate <typename T>
5976d528ed9Sopenharmony_ciclass CheckedNumeric;
5986d528ed9Sopenharmony_ci
5996d528ed9Sopenharmony_citemplate <typename T>
6006d528ed9Sopenharmony_ciclass ClampedNumeric;
6016d528ed9Sopenharmony_ci
6026d528ed9Sopenharmony_citemplate <typename T>
6036d528ed9Sopenharmony_ciclass StrictNumeric;
6046d528ed9Sopenharmony_ci
6056d528ed9Sopenharmony_ci// Used to treat CheckedNumeric and arithmetic underlying types the same.
6066d528ed9Sopenharmony_citemplate <typename T>
6076d528ed9Sopenharmony_cistruct UnderlyingType {
6086d528ed9Sopenharmony_ci  using type = typename ArithmeticOrUnderlyingEnum<T>::type;
6096d528ed9Sopenharmony_ci  static const bool is_numeric = std::is_arithmetic<type>::value;
6106d528ed9Sopenharmony_ci  static const bool is_checked = false;
6116d528ed9Sopenharmony_ci  static const bool is_clamped = false;
6126d528ed9Sopenharmony_ci  static const bool is_strict = false;
6136d528ed9Sopenharmony_ci};
6146d528ed9Sopenharmony_ci
6156d528ed9Sopenharmony_citemplate <typename T>
6166d528ed9Sopenharmony_cistruct UnderlyingType<CheckedNumeric<T>> {
6176d528ed9Sopenharmony_ci  using type = T;
6186d528ed9Sopenharmony_ci  static const bool is_numeric = true;
6196d528ed9Sopenharmony_ci  static const bool is_checked = true;
6206d528ed9Sopenharmony_ci  static const bool is_clamped = false;
6216d528ed9Sopenharmony_ci  static const bool is_strict = false;
6226d528ed9Sopenharmony_ci};
6236d528ed9Sopenharmony_ci
6246d528ed9Sopenharmony_citemplate <typename T>
6256d528ed9Sopenharmony_cistruct UnderlyingType<ClampedNumeric<T>> {
6266d528ed9Sopenharmony_ci  using type = T;
6276d528ed9Sopenharmony_ci  static const bool is_numeric = true;
6286d528ed9Sopenharmony_ci  static const bool is_checked = false;
6296d528ed9Sopenharmony_ci  static const bool is_clamped = true;
6306d528ed9Sopenharmony_ci  static const bool is_strict = false;
6316d528ed9Sopenharmony_ci};
6326d528ed9Sopenharmony_ci
6336d528ed9Sopenharmony_citemplate <typename T>
6346d528ed9Sopenharmony_cistruct UnderlyingType<StrictNumeric<T>> {
6356d528ed9Sopenharmony_ci  using type = T;
6366d528ed9Sopenharmony_ci  static const bool is_numeric = true;
6376d528ed9Sopenharmony_ci  static const bool is_checked = false;
6386d528ed9Sopenharmony_ci  static const bool is_clamped = false;
6396d528ed9Sopenharmony_ci  static const bool is_strict = true;
6406d528ed9Sopenharmony_ci};
6416d528ed9Sopenharmony_ci
6426d528ed9Sopenharmony_citemplate <typename L, typename R>
6436d528ed9Sopenharmony_cistruct IsCheckedOp {
6446d528ed9Sopenharmony_ci  static const bool value =
6456d528ed9Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6466d528ed9Sopenharmony_ci      (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
6476d528ed9Sopenharmony_ci};
6486d528ed9Sopenharmony_ci
6496d528ed9Sopenharmony_citemplate <typename L, typename R>
6506d528ed9Sopenharmony_cistruct IsClampedOp {
6516d528ed9Sopenharmony_ci  static const bool value =
6526d528ed9Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6536d528ed9Sopenharmony_ci      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
6546d528ed9Sopenharmony_ci      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
6556d528ed9Sopenharmony_ci};
6566d528ed9Sopenharmony_ci
6576d528ed9Sopenharmony_citemplate <typename L, typename R>
6586d528ed9Sopenharmony_cistruct IsStrictOp {
6596d528ed9Sopenharmony_ci  static const bool value =
6606d528ed9Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6616d528ed9Sopenharmony_ci      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
6626d528ed9Sopenharmony_ci      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
6636d528ed9Sopenharmony_ci      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
6646d528ed9Sopenharmony_ci};
6656d528ed9Sopenharmony_ci
6666d528ed9Sopenharmony_ci// as_signed<> returns the supplied integral value (or integral castable
6676d528ed9Sopenharmony_ci// Numeric template) cast as a signed integral of equivalent precision.
6686d528ed9Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
6696d528ed9Sopenharmony_citemplate <typename Src>
6706d528ed9Sopenharmony_ciconstexpr typename std::make_signed<
6716d528ed9Sopenharmony_ci    typename base::internal::UnderlyingType<Src>::type>::type
6726d528ed9Sopenharmony_cias_signed(const Src value) {
6736d528ed9Sopenharmony_ci  static_assert(std::is_integral<decltype(as_signed(value))>::value,
6746d528ed9Sopenharmony_ci                "Argument must be a signed or unsigned integer type.");
6756d528ed9Sopenharmony_ci  return static_cast<decltype(as_signed(value))>(value);
6766d528ed9Sopenharmony_ci}
6776d528ed9Sopenharmony_ci
6786d528ed9Sopenharmony_ci// as_unsigned<> returns the supplied integral value (or integral castable
6796d528ed9Sopenharmony_ci// Numeric template) cast as an unsigned integral of equivalent precision.
6806d528ed9Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
6816d528ed9Sopenharmony_citemplate <typename Src>
6826d528ed9Sopenharmony_ciconstexpr typename std::make_unsigned<
6836d528ed9Sopenharmony_ci    typename base::internal::UnderlyingType<Src>::type>::type
6846d528ed9Sopenharmony_cias_unsigned(const Src value) {
6856d528ed9Sopenharmony_ci  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
6866d528ed9Sopenharmony_ci                "Argument must be a signed or unsigned integer type.");
6876d528ed9Sopenharmony_ci  return static_cast<decltype(as_unsigned(value))>(value);
6886d528ed9Sopenharmony_ci}
6896d528ed9Sopenharmony_ci
6906d528ed9Sopenharmony_citemplate <typename L, typename R>
6916d528ed9Sopenharmony_ciconstexpr bool IsLessImpl(const L lhs,
6926d528ed9Sopenharmony_ci                          const R rhs,
6936d528ed9Sopenharmony_ci                          const RangeCheck l_range,
6946d528ed9Sopenharmony_ci                          const RangeCheck r_range) {
6956d528ed9Sopenharmony_ci  return l_range.IsUnderflow() || r_range.IsOverflow() ||
6966d528ed9Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
6976d528ed9Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
6986d528ed9Sopenharmony_ci}
6996d528ed9Sopenharmony_ci
7006d528ed9Sopenharmony_citemplate <typename L, typename R>
7016d528ed9Sopenharmony_cistruct IsLess {
7026d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7036d528ed9Sopenharmony_ci                "Types must be numeric.");
7046d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7056d528ed9Sopenharmony_ci    return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7066d528ed9Sopenharmony_ci                      DstRangeRelationToSrcRange<L>(rhs));
7076d528ed9Sopenharmony_ci  }
7086d528ed9Sopenharmony_ci};
7096d528ed9Sopenharmony_ci
7106d528ed9Sopenharmony_citemplate <typename L, typename R>
7116d528ed9Sopenharmony_ciconstexpr bool IsLessOrEqualImpl(const L lhs,
7126d528ed9Sopenharmony_ci                                 const R rhs,
7136d528ed9Sopenharmony_ci                                 const RangeCheck l_range,
7146d528ed9Sopenharmony_ci                                 const RangeCheck r_range) {
7156d528ed9Sopenharmony_ci  return l_range.IsUnderflow() || r_range.IsOverflow() ||
7166d528ed9Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
7176d528ed9Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
7186d528ed9Sopenharmony_ci}
7196d528ed9Sopenharmony_ci
7206d528ed9Sopenharmony_citemplate <typename L, typename R>
7216d528ed9Sopenharmony_cistruct IsLessOrEqual {
7226d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7236d528ed9Sopenharmony_ci                "Types must be numeric.");
7246d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7256d528ed9Sopenharmony_ci    return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7266d528ed9Sopenharmony_ci                             DstRangeRelationToSrcRange<L>(rhs));
7276d528ed9Sopenharmony_ci  }
7286d528ed9Sopenharmony_ci};
7296d528ed9Sopenharmony_ci
7306d528ed9Sopenharmony_citemplate <typename L, typename R>
7316d528ed9Sopenharmony_ciconstexpr bool IsGreaterImpl(const L lhs,
7326d528ed9Sopenharmony_ci                             const R rhs,
7336d528ed9Sopenharmony_ci                             const RangeCheck l_range,
7346d528ed9Sopenharmony_ci                             const RangeCheck r_range) {
7356d528ed9Sopenharmony_ci  return l_range.IsOverflow() || r_range.IsUnderflow() ||
7366d528ed9Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
7376d528ed9Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
7386d528ed9Sopenharmony_ci}
7396d528ed9Sopenharmony_ci
7406d528ed9Sopenharmony_citemplate <typename L, typename R>
7416d528ed9Sopenharmony_cistruct IsGreater {
7426d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7436d528ed9Sopenharmony_ci                "Types must be numeric.");
7446d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7456d528ed9Sopenharmony_ci    return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7466d528ed9Sopenharmony_ci                         DstRangeRelationToSrcRange<L>(rhs));
7476d528ed9Sopenharmony_ci  }
7486d528ed9Sopenharmony_ci};
7496d528ed9Sopenharmony_ci
7506d528ed9Sopenharmony_citemplate <typename L, typename R>
7516d528ed9Sopenharmony_ciconstexpr bool IsGreaterOrEqualImpl(const L lhs,
7526d528ed9Sopenharmony_ci                                    const R rhs,
7536d528ed9Sopenharmony_ci                                    const RangeCheck l_range,
7546d528ed9Sopenharmony_ci                                    const RangeCheck r_range) {
7556d528ed9Sopenharmony_ci  return l_range.IsOverflow() || r_range.IsUnderflow() ||
7566d528ed9Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
7576d528ed9Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
7586d528ed9Sopenharmony_ci}
7596d528ed9Sopenharmony_ci
7606d528ed9Sopenharmony_citemplate <typename L, typename R>
7616d528ed9Sopenharmony_cistruct IsGreaterOrEqual {
7626d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7636d528ed9Sopenharmony_ci                "Types must be numeric.");
7646d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7656d528ed9Sopenharmony_ci    return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7666d528ed9Sopenharmony_ci                                DstRangeRelationToSrcRange<L>(rhs));
7676d528ed9Sopenharmony_ci  }
7686d528ed9Sopenharmony_ci};
7696d528ed9Sopenharmony_ci
7706d528ed9Sopenharmony_citemplate <typename L, typename R>
7716d528ed9Sopenharmony_cistruct IsEqual {
7726d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7736d528ed9Sopenharmony_ci                "Types must be numeric.");
7746d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7756d528ed9Sopenharmony_ci    return DstRangeRelationToSrcRange<R>(lhs) ==
7766d528ed9Sopenharmony_ci               DstRangeRelationToSrcRange<L>(rhs) &&
7776d528ed9Sopenharmony_ci           static_cast<decltype(lhs + rhs)>(lhs) ==
7786d528ed9Sopenharmony_ci               static_cast<decltype(lhs + rhs)>(rhs);
7796d528ed9Sopenharmony_ci  }
7806d528ed9Sopenharmony_ci};
7816d528ed9Sopenharmony_ci
7826d528ed9Sopenharmony_citemplate <typename L, typename R>
7836d528ed9Sopenharmony_cistruct IsNotEqual {
7846d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7856d528ed9Sopenharmony_ci                "Types must be numeric.");
7866d528ed9Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7876d528ed9Sopenharmony_ci    return DstRangeRelationToSrcRange<R>(lhs) !=
7886d528ed9Sopenharmony_ci               DstRangeRelationToSrcRange<L>(rhs) ||
7896d528ed9Sopenharmony_ci           static_cast<decltype(lhs + rhs)>(lhs) !=
7906d528ed9Sopenharmony_ci               static_cast<decltype(lhs + rhs)>(rhs);
7916d528ed9Sopenharmony_ci  }
7926d528ed9Sopenharmony_ci};
7936d528ed9Sopenharmony_ci
7946d528ed9Sopenharmony_ci// These perform the actual math operations on the CheckedNumerics.
7956d528ed9Sopenharmony_ci// Binary arithmetic operations.
7966d528ed9Sopenharmony_citemplate <template <typename, typename> class C, typename L, typename R>
7976d528ed9Sopenharmony_ciconstexpr bool SafeCompare(const L lhs, const R rhs) {
7986d528ed9Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7996d528ed9Sopenharmony_ci                "Types must be numeric.");
8006d528ed9Sopenharmony_ci  using Promotion = BigEnoughPromotion<L, R>;
8016d528ed9Sopenharmony_ci  using BigType = typename Promotion::type;
8026d528ed9Sopenharmony_ci  return Promotion::is_contained
8036d528ed9Sopenharmony_ci             // Force to a larger type for speed if both are contained.
8046d528ed9Sopenharmony_ci             ? C<BigType, BigType>::Test(
8056d528ed9Sopenharmony_ci                   static_cast<BigType>(static_cast<L>(lhs)),
8066d528ed9Sopenharmony_ci                   static_cast<BigType>(static_cast<R>(rhs)))
8076d528ed9Sopenharmony_ci             // Let the template functions figure it out for mixed types.
8086d528ed9Sopenharmony_ci             : C<L, R>::Test(lhs, rhs);
8096d528ed9Sopenharmony_ci}
8106d528ed9Sopenharmony_ci
8116d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
8126d528ed9Sopenharmony_ciconstexpr bool IsMaxInRangeForNumericType() {
8136d528ed9Sopenharmony_ci  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
8146d528ed9Sopenharmony_ci                                          std::numeric_limits<Src>::max());
8156d528ed9Sopenharmony_ci}
8166d528ed9Sopenharmony_ci
8176d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
8186d528ed9Sopenharmony_ciconstexpr bool IsMinInRangeForNumericType() {
8196d528ed9Sopenharmony_ci  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
8206d528ed9Sopenharmony_ci                                       std::numeric_limits<Src>::lowest());
8216d528ed9Sopenharmony_ci}
8226d528ed9Sopenharmony_ci
8236d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
8246d528ed9Sopenharmony_ciconstexpr Dst CommonMax() {
8256d528ed9Sopenharmony_ci  return !IsMaxInRangeForNumericType<Dst, Src>()
8266d528ed9Sopenharmony_ci             ? Dst(std::numeric_limits<Dst>::max())
8276d528ed9Sopenharmony_ci             : Dst(std::numeric_limits<Src>::max());
8286d528ed9Sopenharmony_ci}
8296d528ed9Sopenharmony_ci
8306d528ed9Sopenharmony_citemplate <typename Dst, typename Src>
8316d528ed9Sopenharmony_ciconstexpr Dst CommonMin() {
8326d528ed9Sopenharmony_ci  return !IsMinInRangeForNumericType<Dst, Src>()
8336d528ed9Sopenharmony_ci             ? Dst(std::numeric_limits<Dst>::lowest())
8346d528ed9Sopenharmony_ci             : Dst(std::numeric_limits<Src>::lowest());
8356d528ed9Sopenharmony_ci}
8366d528ed9Sopenharmony_ci
8376d528ed9Sopenharmony_ci// This is a wrapper to generate return the max or min for a supplied type.
8386d528ed9Sopenharmony_ci// If the argument is false, the returned value is the maximum. If true the
8396d528ed9Sopenharmony_ci// returned value is the minimum.
8406d528ed9Sopenharmony_citemplate <typename Dst, typename Src = Dst>
8416d528ed9Sopenharmony_ciconstexpr Dst CommonMaxOrMin(bool is_min) {
8426d528ed9Sopenharmony_ci  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
8436d528ed9Sopenharmony_ci}
8446d528ed9Sopenharmony_ci
8456d528ed9Sopenharmony_ci}  // namespace internal
8466d528ed9Sopenharmony_ci}  // namespace base
8476d528ed9Sopenharmony_ci
8486d528ed9Sopenharmony_ci#endif  // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
849