11cb0ef41Sopenharmony_ci// Copyright 2014 The Chromium Authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci// Slightly adapted for inclusion in V8. 61cb0ef41Sopenharmony_ci// Copyright 2014 the V8 project authors. All rights reserved. 71cb0ef41Sopenharmony_ci// List of adaptations: 81cb0ef41Sopenharmony_ci// - include guard names 91cb0ef41Sopenharmony_ci// - wrap in v8 namespace 101cb0ef41Sopenharmony_ci// - formatting (git cl format) 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ci#ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 131cb0ef41Sopenharmony_ci#define V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ci#include <stddef.h> 161cb0ef41Sopenharmony_ci#include <stdint.h> 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci#include <limits> 191cb0ef41Sopenharmony_ci#include <type_traits> 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci#if defined(__GNUC__) || defined(__clang__) 221cb0ef41Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) 231cb0ef41Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) 241cb0ef41Sopenharmony_ci#else 251cb0ef41Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) (x) 261cb0ef41Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) (x) 271cb0ef41Sopenharmony_ci#endif 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_cinamespace v8 { 301cb0ef41Sopenharmony_cinamespace base { 311cb0ef41Sopenharmony_cinamespace internal { 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci// The std library doesn't provide a binary max_exponent for integers, however 341cb0ef41Sopenharmony_ci// we can compute an analog using std::numeric_limits<>::digits. 351cb0ef41Sopenharmony_citemplate <typename NumericType> 361cb0ef41Sopenharmony_cistruct MaxExponent { 371cb0ef41Sopenharmony_ci static const int value = std::is_floating_point<NumericType>::value 381cb0ef41Sopenharmony_ci ? std::numeric_limits<NumericType>::max_exponent 391cb0ef41Sopenharmony_ci : std::numeric_limits<NumericType>::digits + 1; 401cb0ef41Sopenharmony_ci}; 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci// The number of bits (including the sign) in an integer. Eliminates sizeof 431cb0ef41Sopenharmony_ci// hacks. 441cb0ef41Sopenharmony_citemplate <typename NumericType> 451cb0ef41Sopenharmony_cistruct IntegerBitsPlusSign { 461cb0ef41Sopenharmony_ci static const int value = std::numeric_limits<NumericType>::digits + 471cb0ef41Sopenharmony_ci std::is_signed<NumericType>::value; 481cb0ef41Sopenharmony_ci}; 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci// Helper templates for integer manipulations. 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_citemplate <typename Integer> 531cb0ef41Sopenharmony_cistruct PositionOfSignBit { 541cb0ef41Sopenharmony_ci static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; 551cb0ef41Sopenharmony_ci}; 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci// Determines if a numeric value is negative without throwing compiler 581cb0ef41Sopenharmony_ci// warnings on: unsigned(value) < 0. 591cb0ef41Sopenharmony_citemplate <typename T, 601cb0ef41Sopenharmony_ci typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> 611cb0ef41Sopenharmony_ciconstexpr bool IsValueNegative(T value) { 621cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); 631cb0ef41Sopenharmony_ci return value < 0; 641cb0ef41Sopenharmony_ci} 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_citemplate <typename T, 671cb0ef41Sopenharmony_ci typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> 681cb0ef41Sopenharmony_ciconstexpr bool IsValueNegative(T) { 691cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); 701cb0ef41Sopenharmony_ci return false; 711cb0ef41Sopenharmony_ci} 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci// This performs a fast negation, returning a signed value. It works on unsigned 741cb0ef41Sopenharmony_ci// arguments, but probably doesn't do what you want for any unsigned value 751cb0ef41Sopenharmony_ci// larger than max / 2 + 1 (i.e. signed min cast to unsigned). 761cb0ef41Sopenharmony_citemplate <typename T> 771cb0ef41Sopenharmony_ciconstexpr typename std::make_signed<T>::type ConditionalNegate( 781cb0ef41Sopenharmony_ci T x, bool is_negative) { 791cb0ef41Sopenharmony_ci static_assert(std::is_integral<T>::value, "Type must be integral"); 801cb0ef41Sopenharmony_ci using SignedT = typename std::make_signed<T>::type; 811cb0ef41Sopenharmony_ci using UnsignedT = typename std::make_unsigned<T>::type; 821cb0ef41Sopenharmony_ci return static_cast<SignedT>( 831cb0ef41Sopenharmony_ci (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative); 841cb0ef41Sopenharmony_ci} 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci// This performs a safe, absolute value via unsigned overflow. 871cb0ef41Sopenharmony_citemplate <typename T> 881cb0ef41Sopenharmony_ciconstexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { 891cb0ef41Sopenharmony_ci static_assert(std::is_integral<T>::value, "Type must be integral"); 901cb0ef41Sopenharmony_ci using UnsignedT = typename std::make_unsigned<T>::type; 911cb0ef41Sopenharmony_ci return IsValueNegative(value) 921cb0ef41Sopenharmony_ci ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) 931cb0ef41Sopenharmony_ci : static_cast<UnsignedT>(value); 941cb0ef41Sopenharmony_ci} 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci// This allows us to switch paths on known compile-time constants. 971cb0ef41Sopenharmony_ci#if defined(__clang__) || defined(__GNUC__) 981cb0ef41Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() { return true; } 991cb0ef41Sopenharmony_citemplate <typename T> 1001cb0ef41Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T v) { 1011cb0ef41Sopenharmony_ci return __builtin_constant_p(v); 1021cb0ef41Sopenharmony_ci} 1031cb0ef41Sopenharmony_ci#else 1041cb0ef41Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() { return false; } 1051cb0ef41Sopenharmony_citemplate <typename T> 1061cb0ef41Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T) { 1071cb0ef41Sopenharmony_ci return false; 1081cb0ef41Sopenharmony_ci} 1091cb0ef41Sopenharmony_ci#endif 1101cb0ef41Sopenharmony_citemplate <typename T> 1111cb0ef41Sopenharmony_ciconstexpr bool MustTreatAsConstexpr(const T v) { 1121cb0ef41Sopenharmony_ci // Either we can't detect a compile-time constant, and must always use the 1131cb0ef41Sopenharmony_ci // constexpr path, or we know we have a compile-time constant. 1141cb0ef41Sopenharmony_ci return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v); 1151cb0ef41Sopenharmony_ci} 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci// Forces a crash, like a CHECK(false). Used for numeric boundary errors. 1181cb0ef41Sopenharmony_ci// Also used in a constexpr template to trigger a compilation failure on 1191cb0ef41Sopenharmony_ci// an error condition. 1201cb0ef41Sopenharmony_cistruct CheckOnFailure { 1211cb0ef41Sopenharmony_ci template <typename T> 1221cb0ef41Sopenharmony_ci static T HandleFailure() { 1231cb0ef41Sopenharmony_ci#if defined(_MSC_VER) 1241cb0ef41Sopenharmony_ci __debugbreak(); 1251cb0ef41Sopenharmony_ci#elif defined(__GNUC__) || defined(__clang__) 1261cb0ef41Sopenharmony_ci __builtin_trap(); 1271cb0ef41Sopenharmony_ci#else 1281cb0ef41Sopenharmony_ci ((void)(*(volatile char*)0 = 0)); 1291cb0ef41Sopenharmony_ci#endif 1301cb0ef41Sopenharmony_ci return T(); 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci}; 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_cienum IntegerRepresentation { 1351cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_UNSIGNED, 1361cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_SIGNED 1371cb0ef41Sopenharmony_ci}; 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci// A range for a given nunmeric Src type is contained for a given numeric Dst 1401cb0ef41Sopenharmony_ci// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and 1411cb0ef41Sopenharmony_ci// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. 1421cb0ef41Sopenharmony_ci// We implement this as template specializations rather than simple static 1431cb0ef41Sopenharmony_ci// comparisons to ensure type correctness in our comparisons. 1441cb0ef41Sopenharmony_cienum NumericRangeRepresentation { 1451cb0ef41Sopenharmony_ci NUMERIC_RANGE_NOT_CONTAINED, 1461cb0ef41Sopenharmony_ci NUMERIC_RANGE_CONTAINED 1471cb0ef41Sopenharmony_ci}; 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci// Helper templates to statically determine if our destination type can contain 1501cb0ef41Sopenharmony_ci// maximum and minimum values represented by the source type. 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, 1531cb0ef41Sopenharmony_ci IntegerRepresentation DstSign = std::is_signed<Dst>::value 1541cb0ef41Sopenharmony_ci ? INTEGER_REPRESENTATION_SIGNED 1551cb0ef41Sopenharmony_ci : INTEGER_REPRESENTATION_UNSIGNED, 1561cb0ef41Sopenharmony_ci IntegerRepresentation SrcSign = std::is_signed<Src>::value 1571cb0ef41Sopenharmony_ci ? INTEGER_REPRESENTATION_SIGNED 1581cb0ef41Sopenharmony_ci : INTEGER_REPRESENTATION_UNSIGNED> 1591cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange; 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ci// Same sign: Dst is guaranteed to contain Src only if its range is equal or 1621cb0ef41Sopenharmony_ci// larger. 1631cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, IntegerRepresentation Sign> 1641cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { 1651cb0ef41Sopenharmony_ci static const NumericRangeRepresentation value = 1661cb0ef41Sopenharmony_ci MaxExponent<Dst>::value >= MaxExponent<Src>::value 1671cb0ef41Sopenharmony_ci ? NUMERIC_RANGE_CONTAINED 1681cb0ef41Sopenharmony_ci : NUMERIC_RANGE_NOT_CONTAINED; 1691cb0ef41Sopenharmony_ci}; 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci// Unsigned to signed: Dst is guaranteed to contain source only if its range is 1721cb0ef41Sopenharmony_ci// larger. 1731cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 1741cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst, 1751cb0ef41Sopenharmony_ci Src, 1761cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_SIGNED, 1771cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_UNSIGNED> { 1781cb0ef41Sopenharmony_ci static const NumericRangeRepresentation value = 1791cb0ef41Sopenharmony_ci MaxExponent<Dst>::value > MaxExponent<Src>::value 1801cb0ef41Sopenharmony_ci ? NUMERIC_RANGE_CONTAINED 1811cb0ef41Sopenharmony_ci : NUMERIC_RANGE_NOT_CONTAINED; 1821cb0ef41Sopenharmony_ci}; 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci// Signed to unsigned: Dst cannot be statically determined to contain Src. 1851cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 1861cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst, 1871cb0ef41Sopenharmony_ci Src, 1881cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_UNSIGNED, 1891cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_SIGNED> { 1901cb0ef41Sopenharmony_ci static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 1911cb0ef41Sopenharmony_ci}; 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci// This class wraps the range constraints as separate booleans so the compiler 1941cb0ef41Sopenharmony_ci// can identify constants and eliminate unused code paths. 1951cb0ef41Sopenharmony_ciclass RangeCheck { 1961cb0ef41Sopenharmony_ci public: 1971cb0ef41Sopenharmony_ci constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) 1981cb0ef41Sopenharmony_ci : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} 1991cb0ef41Sopenharmony_ci constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {} 2001cb0ef41Sopenharmony_ci constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } 2011cb0ef41Sopenharmony_ci constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } 2021cb0ef41Sopenharmony_ci constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } 2031cb0ef41Sopenharmony_ci constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } 2041cb0ef41Sopenharmony_ci constexpr bool IsOverflowFlagSet() const { return is_overflow_; } 2051cb0ef41Sopenharmony_ci constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } 2061cb0ef41Sopenharmony_ci constexpr bool operator==(const RangeCheck rhs) const { 2071cb0ef41Sopenharmony_ci return is_underflow_ == rhs.is_underflow_ && 2081cb0ef41Sopenharmony_ci is_overflow_ == rhs.is_overflow_; 2091cb0ef41Sopenharmony_ci } 2101cb0ef41Sopenharmony_ci constexpr bool operator!=(const RangeCheck rhs) const { 2111cb0ef41Sopenharmony_ci return !(*this == rhs); 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci private: 2151cb0ef41Sopenharmony_ci // Do not change the order of these member variables. The integral conversion 2161cb0ef41Sopenharmony_ci // optimization depends on this exact order. 2171cb0ef41Sopenharmony_ci const bool is_underflow_; 2181cb0ef41Sopenharmony_ci const bool is_overflow_; 2191cb0ef41Sopenharmony_ci}; 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci// The following helper template addresses a corner case in range checks for 2221cb0ef41Sopenharmony_ci// conversion from a floating-point type to an integral type of smaller range 2231cb0ef41Sopenharmony_ci// but larger precision (e.g. float -> unsigned). The problem is as follows: 2241cb0ef41Sopenharmony_ci// 1. Integral maximum is always one less than a power of two, so it must be 2251cb0ef41Sopenharmony_ci// truncated to fit the mantissa of the floating point. The direction of 2261cb0ef41Sopenharmony_ci// rounding is implementation defined, but by default it's always IEEE 2271cb0ef41Sopenharmony_ci// floats, which round to nearest and thus result in a value of larger 2281cb0ef41Sopenharmony_ci// magnitude than the integral value. 2291cb0ef41Sopenharmony_ci// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX 2301cb0ef41Sopenharmony_ci// // is 4294967295u. 2311cb0ef41Sopenharmony_ci// 2. If the floating point value is equal to the promoted integral maximum 2321cb0ef41Sopenharmony_ci// value, a range check will erroneously pass. 2331cb0ef41Sopenharmony_ci// Example: (4294967296f <= 4294967295u) // This is true due to a precision 2341cb0ef41Sopenharmony_ci// // loss in rounding up to float. 2351cb0ef41Sopenharmony_ci// 3. When the floating point value is then converted to an integral, the 2361cb0ef41Sopenharmony_ci// resulting value is out of range for the target integral type and 2371cb0ef41Sopenharmony_ci// thus is implementation defined. 2381cb0ef41Sopenharmony_ci// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. 2391cb0ef41Sopenharmony_ci// To fix this bug we manually truncate the maximum value when the destination 2401cb0ef41Sopenharmony_ci// type is an integral of larger precision than the source floating-point type, 2411cb0ef41Sopenharmony_ci// such that the resulting maximum is represented exactly as a floating point. 2421cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds> 2431cb0ef41Sopenharmony_cistruct NarrowingRange { 2441cb0ef41Sopenharmony_ci using SrcLimits = std::numeric_limits<Src>; 2451cb0ef41Sopenharmony_ci using DstLimits = typename std::numeric_limits<Dst>; 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci // Computes the mask required to make an accurate comparison between types. 2481cb0ef41Sopenharmony_ci static const int kShift = 2491cb0ef41Sopenharmony_ci (MaxExponent<Src>::value > MaxExponent<Dst>::value && 2501cb0ef41Sopenharmony_ci SrcLimits::digits < DstLimits::digits) 2511cb0ef41Sopenharmony_ci ? (DstLimits::digits - SrcLimits::digits) 2521cb0ef41Sopenharmony_ci : 0; 2531cb0ef41Sopenharmony_ci template <typename T, typename std::enable_if< 2541cb0ef41Sopenharmony_ci std::is_integral<T>::value>::type* = nullptr> 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci // Masks out the integer bits that are beyond the precision of the 2571cb0ef41Sopenharmony_ci // intermediate type used for comparison. 2581cb0ef41Sopenharmony_ci static constexpr T Adjust(T value) { 2591cb0ef41Sopenharmony_ci static_assert(std::is_same<T, Dst>::value, ""); 2601cb0ef41Sopenharmony_ci static_assert(kShift < DstLimits::digits, ""); 2611cb0ef41Sopenharmony_ci return static_cast<T>( 2621cb0ef41Sopenharmony_ci ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), 2631cb0ef41Sopenharmony_ci IsValueNegative(value))); 2641cb0ef41Sopenharmony_ci } 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci template <typename T, typename std::enable_if< 2671cb0ef41Sopenharmony_ci std::is_floating_point<T>::value>::type* = nullptr> 2681cb0ef41Sopenharmony_ci static constexpr T Adjust(T value) { 2691cb0ef41Sopenharmony_ci static_assert(std::is_same<T, Dst>::value, ""); 2701cb0ef41Sopenharmony_ci static_assert(kShift == 0, ""); 2711cb0ef41Sopenharmony_ci return value; 2721cb0ef41Sopenharmony_ci } 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } 2751cb0ef41Sopenharmony_ci static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } 2761cb0ef41Sopenharmony_ci}; 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds, 2791cb0ef41Sopenharmony_ci IntegerRepresentation DstSign = std::is_signed<Dst>::value 2801cb0ef41Sopenharmony_ci ? INTEGER_REPRESENTATION_SIGNED 2811cb0ef41Sopenharmony_ci : INTEGER_REPRESENTATION_UNSIGNED, 2821cb0ef41Sopenharmony_ci IntegerRepresentation SrcSign = std::is_signed<Src>::value 2831cb0ef41Sopenharmony_ci ? INTEGER_REPRESENTATION_SIGNED 2841cb0ef41Sopenharmony_ci : INTEGER_REPRESENTATION_UNSIGNED, 2851cb0ef41Sopenharmony_ci NumericRangeRepresentation DstRange = 2861cb0ef41Sopenharmony_ci StaticDstRangeRelationToSrcRange<Dst, Src>::value> 2871cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl; 2881cb0ef41Sopenharmony_ci 2891cb0ef41Sopenharmony_ci// The following templates are for ranges that must be verified at runtime. We 2901cb0ef41Sopenharmony_ci// split it into checks based on signedness to avoid confusing casts and 2911cb0ef41Sopenharmony_ci// compiler warnings on signed an unsigned comparisons. 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci// Same sign narrowing: The range is contained for normal limits. 2941cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds, 2951cb0ef41Sopenharmony_ci IntegerRepresentation DstSign, IntegerRepresentation SrcSign> 2961cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds, DstSign, SrcSign, 2971cb0ef41Sopenharmony_ci NUMERIC_RANGE_CONTAINED> { 2981cb0ef41Sopenharmony_ci static constexpr RangeCheck Check(Src value) { 2991cb0ef41Sopenharmony_ci using SrcLimits = std::numeric_limits<Src>; 3001cb0ef41Sopenharmony_ci using DstLimits = NarrowingRange<Dst, Src, Bounds>; 3011cb0ef41Sopenharmony_ci return RangeCheck( 3021cb0ef41Sopenharmony_ci static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || 3031cb0ef41Sopenharmony_ci static_cast<Dst>(value) >= DstLimits::lowest(), 3041cb0ef41Sopenharmony_ci static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || 3051cb0ef41Sopenharmony_ci static_cast<Dst>(value) <= DstLimits::max()); 3061cb0ef41Sopenharmony_ci } 3071cb0ef41Sopenharmony_ci}; 3081cb0ef41Sopenharmony_ci 3091cb0ef41Sopenharmony_ci// Signed to signed narrowing: Both the upper and lower boundaries may be 3101cb0ef41Sopenharmony_ci// exceeded for standard limits. 3111cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds> 3121cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl< 3131cb0ef41Sopenharmony_ci Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, 3141cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 3151cb0ef41Sopenharmony_ci static constexpr RangeCheck Check(Src value) { 3161cb0ef41Sopenharmony_ci using DstLimits = NarrowingRange<Dst, Src, Bounds>; 3171cb0ef41Sopenharmony_ci return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); 3181cb0ef41Sopenharmony_ci } 3191cb0ef41Sopenharmony_ci}; 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for 3221cb0ef41Sopenharmony_ci// standard limits. 3231cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds> 3241cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl< 3251cb0ef41Sopenharmony_ci Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, 3261cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 3271cb0ef41Sopenharmony_ci static constexpr RangeCheck Check(Src value) { 3281cb0ef41Sopenharmony_ci using DstLimits = NarrowingRange<Dst, Src, Bounds>; 3291cb0ef41Sopenharmony_ci return RangeCheck( 3301cb0ef41Sopenharmony_ci DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), 3311cb0ef41Sopenharmony_ci value <= DstLimits::max()); 3321cb0ef41Sopenharmony_ci } 3331cb0ef41Sopenharmony_ci}; 3341cb0ef41Sopenharmony_ci 3351cb0ef41Sopenharmony_ci// Unsigned to signed: Only the upper bound can be exceeded for standard limits. 3361cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds> 3371cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl< 3381cb0ef41Sopenharmony_ci Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, 3391cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 3401cb0ef41Sopenharmony_ci static constexpr RangeCheck Check(Src value) { 3411cb0ef41Sopenharmony_ci using DstLimits = NarrowingRange<Dst, Src, Bounds>; 3421cb0ef41Sopenharmony_ci using Promotion = decltype(Src() + Dst()); 3431cb0ef41Sopenharmony_ci return RangeCheck(DstLimits::lowest() <= Dst(0) || 3441cb0ef41Sopenharmony_ci static_cast<Promotion>(value) >= 3451cb0ef41Sopenharmony_ci static_cast<Promotion>(DstLimits::lowest()), 3461cb0ef41Sopenharmony_ci static_cast<Promotion>(value) <= 3471cb0ef41Sopenharmony_ci static_cast<Promotion>(DstLimits::max())); 3481cb0ef41Sopenharmony_ci } 3491cb0ef41Sopenharmony_ci}; 3501cb0ef41Sopenharmony_ci 3511cb0ef41Sopenharmony_ci// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, 3521cb0ef41Sopenharmony_ci// and any negative value exceeds the lower boundary for standard limits. 3531cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds> 3541cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl< 3551cb0ef41Sopenharmony_ci Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, 3561cb0ef41Sopenharmony_ci INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 3571cb0ef41Sopenharmony_ci static constexpr RangeCheck Check(Src value) { 3581cb0ef41Sopenharmony_ci using SrcLimits = std::numeric_limits<Src>; 3591cb0ef41Sopenharmony_ci using DstLimits = NarrowingRange<Dst, Src, Bounds>; 3601cb0ef41Sopenharmony_ci using Promotion = decltype(Src() + Dst()); 3611cb0ef41Sopenharmony_ci bool ge_zero = false; 3621cb0ef41Sopenharmony_ci // Converting floating-point to integer will discard fractional part, so 3631cb0ef41Sopenharmony_ci // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. 3641cb0ef41Sopenharmony_ci if (std::is_floating_point<Src>::value) { 3651cb0ef41Sopenharmony_ci ge_zero = value > Src(-1); 3661cb0ef41Sopenharmony_ci } else { 3671cb0ef41Sopenharmony_ci ge_zero = value >= Src(0); 3681cb0ef41Sopenharmony_ci } 3691cb0ef41Sopenharmony_ci return RangeCheck( 3701cb0ef41Sopenharmony_ci ge_zero && (DstLimits::lowest() == 0 || 3711cb0ef41Sopenharmony_ci static_cast<Dst>(value) >= DstLimits::lowest()), 3721cb0ef41Sopenharmony_ci static_cast<Promotion>(SrcLimits::max()) <= 3731cb0ef41Sopenharmony_ci static_cast<Promotion>(DstLimits::max()) || 3741cb0ef41Sopenharmony_ci static_cast<Promotion>(value) <= 3751cb0ef41Sopenharmony_ci static_cast<Promotion>(DstLimits::max())); 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci}; 3781cb0ef41Sopenharmony_ci 3791cb0ef41Sopenharmony_ci// Simple wrapper for statically checking if a type's range is contained. 3801cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 3811cb0ef41Sopenharmony_cistruct IsTypeInRangeForNumericType { 3821cb0ef41Sopenharmony_ci static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == 3831cb0ef41Sopenharmony_ci NUMERIC_RANGE_CONTAINED; 3841cb0ef41Sopenharmony_ci}; 3851cb0ef41Sopenharmony_ci 3861cb0ef41Sopenharmony_citemplate <typename Dst, template <typename> class Bounds = std::numeric_limits, 3871cb0ef41Sopenharmony_ci typename Src> 3881cb0ef41Sopenharmony_ciconstexpr RangeCheck DstRangeRelationToSrcRange(Src value) { 3891cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); 3901cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); 3911cb0ef41Sopenharmony_ci static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); 3921cb0ef41Sopenharmony_ci return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); 3931cb0ef41Sopenharmony_ci} 3941cb0ef41Sopenharmony_ci 3951cb0ef41Sopenharmony_ci// Integer promotion templates used by the portable checked integer arithmetic. 3961cb0ef41Sopenharmony_citemplate <size_t Size, bool IsSigned> 3971cb0ef41Sopenharmony_cistruct IntegerForDigitsAndSign; 3981cb0ef41Sopenharmony_ci 3991cb0ef41Sopenharmony_ci#define INTEGER_FOR_DIGITS_AND_SIGN(I) \ 4001cb0ef41Sopenharmony_ci template <> \ 4011cb0ef41Sopenharmony_ci struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ 4021cb0ef41Sopenharmony_ci std::is_signed<I>::value> { \ 4031cb0ef41Sopenharmony_ci using type = I; \ 4041cb0ef41Sopenharmony_ci } 4051cb0ef41Sopenharmony_ci 4061cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int8_t); 4071cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint8_t); 4081cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int16_t); 4091cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint16_t); 4101cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int32_t); 4111cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint32_t); 4121cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int64_t); 4131cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint64_t); 4141cb0ef41Sopenharmony_ci#undef INTEGER_FOR_DIGITS_AND_SIGN 4151cb0ef41Sopenharmony_ci 4161cb0ef41Sopenharmony_ci// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to 4171cb0ef41Sopenharmony_ci// support 128-bit math, then the ArithmeticPromotion template below will need 4181cb0ef41Sopenharmony_ci// to be updated (or more likely replaced with a decltype expression). 4191cb0ef41Sopenharmony_cistatic_assert(IntegerBitsPlusSign<intmax_t>::value == 64, 4201cb0ef41Sopenharmony_ci "Max integer size not supported for this toolchain."); 4211cb0ef41Sopenharmony_ci 4221cb0ef41Sopenharmony_citemplate <typename Integer, bool IsSigned = std::is_signed<Integer>::value> 4231cb0ef41Sopenharmony_cistruct TwiceWiderInteger { 4241cb0ef41Sopenharmony_ci using type = 4251cb0ef41Sopenharmony_ci typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, 4261cb0ef41Sopenharmony_ci IsSigned>::type; 4271cb0ef41Sopenharmony_ci}; 4281cb0ef41Sopenharmony_ci 4291cb0ef41Sopenharmony_cienum ArithmeticPromotionCategory { 4301cb0ef41Sopenharmony_ci LEFT_PROMOTION, // Use the type of the left-hand argument. 4311cb0ef41Sopenharmony_ci RIGHT_PROMOTION // Use the type of the right-hand argument. 4321cb0ef41Sopenharmony_ci}; 4331cb0ef41Sopenharmony_ci 4341cb0ef41Sopenharmony_ci// Determines the type that can represent the largest positive value. 4351cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs, 4361cb0ef41Sopenharmony_ci ArithmeticPromotionCategory Promotion = 4371cb0ef41Sopenharmony_ci (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) 4381cb0ef41Sopenharmony_ci ? LEFT_PROMOTION 4391cb0ef41Sopenharmony_ci : RIGHT_PROMOTION> 4401cb0ef41Sopenharmony_cistruct MaxExponentPromotion; 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 4431cb0ef41Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { 4441cb0ef41Sopenharmony_ci using type = Lhs; 4451cb0ef41Sopenharmony_ci}; 4461cb0ef41Sopenharmony_ci 4471cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 4481cb0ef41Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { 4491cb0ef41Sopenharmony_ci using type = Rhs; 4501cb0ef41Sopenharmony_ci}; 4511cb0ef41Sopenharmony_ci 4521cb0ef41Sopenharmony_ci// Determines the type that can represent the lowest arithmetic value. 4531cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs, 4541cb0ef41Sopenharmony_ci ArithmeticPromotionCategory Promotion = 4551cb0ef41Sopenharmony_ci std::is_signed<Lhs>::value 4561cb0ef41Sopenharmony_ci ? (std::is_signed<Rhs>::value 4571cb0ef41Sopenharmony_ci ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value 4581cb0ef41Sopenharmony_ci ? LEFT_PROMOTION 4591cb0ef41Sopenharmony_ci : RIGHT_PROMOTION) 4601cb0ef41Sopenharmony_ci : LEFT_PROMOTION) 4611cb0ef41Sopenharmony_ci : (std::is_signed<Rhs>::value 4621cb0ef41Sopenharmony_ci ? RIGHT_PROMOTION 4631cb0ef41Sopenharmony_ci : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value 4641cb0ef41Sopenharmony_ci ? LEFT_PROMOTION 4651cb0ef41Sopenharmony_ci : RIGHT_PROMOTION))> 4661cb0ef41Sopenharmony_cistruct LowestValuePromotion; 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 4691cb0ef41Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { 4701cb0ef41Sopenharmony_ci using type = Lhs; 4711cb0ef41Sopenharmony_ci}; 4721cb0ef41Sopenharmony_ci 4731cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 4741cb0ef41Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { 4751cb0ef41Sopenharmony_ci using type = Rhs; 4761cb0ef41Sopenharmony_ci}; 4771cb0ef41Sopenharmony_ci 4781cb0ef41Sopenharmony_ci// Determines the type that is best able to represent an arithmetic result. 4791cb0ef41Sopenharmony_citemplate < 4801cb0ef41Sopenharmony_ci typename Lhs, typename Rhs = Lhs, 4811cb0ef41Sopenharmony_ci bool is_intmax_type = 4821cb0ef41Sopenharmony_ci std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&& 4831cb0ef41Sopenharmony_ci IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: 4841cb0ef41Sopenharmony_ci value == IntegerBitsPlusSign<intmax_t>::value, 4851cb0ef41Sopenharmony_ci bool is_max_exponent = 4861cb0ef41Sopenharmony_ci StaticDstRangeRelationToSrcRange< 4871cb0ef41Sopenharmony_ci typename MaxExponentPromotion<Lhs, Rhs>::type, Lhs>::value == 4881cb0ef41Sopenharmony_ci NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< 4891cb0ef41Sopenharmony_ci typename MaxExponentPromotion<Lhs, Rhs>::type, Rhs>::value == 4901cb0ef41Sopenharmony_ci NUMERIC_RANGE_CONTAINED> 4911cb0ef41Sopenharmony_cistruct BigEnoughPromotion; 4921cb0ef41Sopenharmony_ci 4931cb0ef41Sopenharmony_ci// The side with the max exponent is big enough. 4941cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs, bool is_intmax_type> 4951cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { 4961cb0ef41Sopenharmony_ci using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 4971cb0ef41Sopenharmony_ci static const bool is_contained = true; 4981cb0ef41Sopenharmony_ci}; 4991cb0ef41Sopenharmony_ci 5001cb0ef41Sopenharmony_ci// We can use a twice wider type to fit. 5011cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 5021cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, false, false> { 5031cb0ef41Sopenharmony_ci using type = 5041cb0ef41Sopenharmony_ci typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 5051cb0ef41Sopenharmony_ci std::is_signed<Lhs>::value || 5061cb0ef41Sopenharmony_ci std::is_signed<Rhs>::value>::type; 5071cb0ef41Sopenharmony_ci static const bool is_contained = true; 5081cb0ef41Sopenharmony_ci}; 5091cb0ef41Sopenharmony_ci 5101cb0ef41Sopenharmony_ci// No type is large enough. 5111cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 5121cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, true, false> { 5131cb0ef41Sopenharmony_ci using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 5141cb0ef41Sopenharmony_ci static const bool is_contained = false; 5151cb0ef41Sopenharmony_ci}; 5161cb0ef41Sopenharmony_ci 5171cb0ef41Sopenharmony_ci// We can statically check if operations on the provided types can wrap, so we 5181cb0ef41Sopenharmony_ci// can skip the checked operations if they're not needed. So, for an integer we 5191cb0ef41Sopenharmony_ci// care if the destination type preserves the sign and is twice the width of 5201cb0ef41Sopenharmony_ci// the source. 5211cb0ef41Sopenharmony_citemplate <typename T, typename Lhs, typename Rhs = Lhs> 5221cb0ef41Sopenharmony_cistruct IsIntegerArithmeticSafe { 5231cb0ef41Sopenharmony_ci static const bool value = 5241cb0ef41Sopenharmony_ci !std::is_floating_point<T>::value && 5251cb0ef41Sopenharmony_ci !std::is_floating_point<Lhs>::value && 5261cb0ef41Sopenharmony_ci !std::is_floating_point<Rhs>::value && 5271cb0ef41Sopenharmony_ci std::is_signed<T>::value >= std::is_signed<Lhs>::value && 5281cb0ef41Sopenharmony_ci IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && 5291cb0ef41Sopenharmony_ci std::is_signed<T>::value >= std::is_signed<Rhs>::value && 5301cb0ef41Sopenharmony_ci IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); 5311cb0ef41Sopenharmony_ci}; 5321cb0ef41Sopenharmony_ci 5331cb0ef41Sopenharmony_ci// Promotes to a type that can represent any possible result of a binary 5341cb0ef41Sopenharmony_ci// arithmetic operation with the source types. 5351cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs, 5361cb0ef41Sopenharmony_ci bool is_promotion_possible = IsIntegerArithmeticSafe< 5371cb0ef41Sopenharmony_ci typename std::conditional<std::is_signed<Lhs>::value || 5381cb0ef41Sopenharmony_ci std::is_signed<Rhs>::value, 5391cb0ef41Sopenharmony_ci intmax_t, uintmax_t>::type, 5401cb0ef41Sopenharmony_ci typename MaxExponentPromotion<Lhs, Rhs>::type>::value> 5411cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion; 5421cb0ef41Sopenharmony_ci 5431cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 5441cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, true> { 5451cb0ef41Sopenharmony_ci using type = 5461cb0ef41Sopenharmony_ci typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 5471cb0ef41Sopenharmony_ci std::is_signed<Lhs>::value || 5481cb0ef41Sopenharmony_ci std::is_signed<Rhs>::value>::type; 5491cb0ef41Sopenharmony_ci static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, ""); 5501cb0ef41Sopenharmony_ci static const bool is_contained = true; 5511cb0ef41Sopenharmony_ci}; 5521cb0ef41Sopenharmony_ci 5531cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs> 5541cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, false> { 5551cb0ef41Sopenharmony_ci using type = typename BigEnoughPromotion<Lhs, Rhs>::type; 5561cb0ef41Sopenharmony_ci static const bool is_contained = false; 5571cb0ef41Sopenharmony_ci}; 5581cb0ef41Sopenharmony_ci 5591cb0ef41Sopenharmony_ci// Extracts the underlying type from an enum. 5601cb0ef41Sopenharmony_citemplate <typename T, bool is_enum = std::is_enum<T>::value> 5611cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum; 5621cb0ef41Sopenharmony_ci 5631cb0ef41Sopenharmony_citemplate <typename T> 5641cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, true> { 5651cb0ef41Sopenharmony_ci using type = typename std::underlying_type<T>::type; 5661cb0ef41Sopenharmony_ci static const bool value = std::is_arithmetic<type>::value; 5671cb0ef41Sopenharmony_ci}; 5681cb0ef41Sopenharmony_ci 5691cb0ef41Sopenharmony_citemplate <typename T> 5701cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, false> { 5711cb0ef41Sopenharmony_ci using type = T; 5721cb0ef41Sopenharmony_ci static const bool value = std::is_arithmetic<type>::value; 5731cb0ef41Sopenharmony_ci}; 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci// The following are helper templates used in the CheckedNumeric class. 5761cb0ef41Sopenharmony_citemplate <typename T> 5771cb0ef41Sopenharmony_ciclass CheckedNumeric; 5781cb0ef41Sopenharmony_ci 5791cb0ef41Sopenharmony_citemplate <typename T> 5801cb0ef41Sopenharmony_ciclass ClampedNumeric; 5811cb0ef41Sopenharmony_ci 5821cb0ef41Sopenharmony_citemplate <typename T> 5831cb0ef41Sopenharmony_ciclass StrictNumeric; 5841cb0ef41Sopenharmony_ci 5851cb0ef41Sopenharmony_ci// Used to treat CheckedNumeric and arithmetic underlying types the same. 5861cb0ef41Sopenharmony_citemplate <typename T> 5871cb0ef41Sopenharmony_cistruct UnderlyingType { 5881cb0ef41Sopenharmony_ci using type = typename ArithmeticOrUnderlyingEnum<T>::type; 5891cb0ef41Sopenharmony_ci static const bool is_numeric = std::is_arithmetic<type>::value; 5901cb0ef41Sopenharmony_ci static const bool is_checked = false; 5911cb0ef41Sopenharmony_ci static const bool is_clamped = false; 5921cb0ef41Sopenharmony_ci static const bool is_strict = false; 5931cb0ef41Sopenharmony_ci}; 5941cb0ef41Sopenharmony_ci 5951cb0ef41Sopenharmony_citemplate <typename T> 5961cb0ef41Sopenharmony_cistruct UnderlyingType<CheckedNumeric<T>> { 5971cb0ef41Sopenharmony_ci using type = T; 5981cb0ef41Sopenharmony_ci static const bool is_numeric = true; 5991cb0ef41Sopenharmony_ci static const bool is_checked = true; 6001cb0ef41Sopenharmony_ci static const bool is_clamped = false; 6011cb0ef41Sopenharmony_ci static const bool is_strict = false; 6021cb0ef41Sopenharmony_ci}; 6031cb0ef41Sopenharmony_ci 6041cb0ef41Sopenharmony_citemplate <typename T> 6051cb0ef41Sopenharmony_cistruct UnderlyingType<ClampedNumeric<T>> { 6061cb0ef41Sopenharmony_ci using type = T; 6071cb0ef41Sopenharmony_ci static const bool is_numeric = true; 6081cb0ef41Sopenharmony_ci static const bool is_checked = false; 6091cb0ef41Sopenharmony_ci static const bool is_clamped = true; 6101cb0ef41Sopenharmony_ci static const bool is_strict = false; 6111cb0ef41Sopenharmony_ci}; 6121cb0ef41Sopenharmony_ci 6131cb0ef41Sopenharmony_citemplate <typename T> 6141cb0ef41Sopenharmony_cistruct UnderlyingType<StrictNumeric<T>> { 6151cb0ef41Sopenharmony_ci using type = T; 6161cb0ef41Sopenharmony_ci static const bool is_numeric = true; 6171cb0ef41Sopenharmony_ci static const bool is_checked = false; 6181cb0ef41Sopenharmony_ci static const bool is_clamped = false; 6191cb0ef41Sopenharmony_ci static const bool is_strict = true; 6201cb0ef41Sopenharmony_ci}; 6211cb0ef41Sopenharmony_ci 6221cb0ef41Sopenharmony_citemplate <typename L, typename R> 6231cb0ef41Sopenharmony_cistruct IsCheckedOp { 6241cb0ef41Sopenharmony_ci static const bool value = 6251cb0ef41Sopenharmony_ci UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 6261cb0ef41Sopenharmony_ci (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 6271cb0ef41Sopenharmony_ci}; 6281cb0ef41Sopenharmony_ci 6291cb0ef41Sopenharmony_citemplate <typename L, typename R> 6301cb0ef41Sopenharmony_cistruct IsClampedOp { 6311cb0ef41Sopenharmony_ci static const bool value = 6321cb0ef41Sopenharmony_ci UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 6331cb0ef41Sopenharmony_ci (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) && 6341cb0ef41Sopenharmony_ci !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 6351cb0ef41Sopenharmony_ci}; 6361cb0ef41Sopenharmony_ci 6371cb0ef41Sopenharmony_citemplate <typename L, typename R> 6381cb0ef41Sopenharmony_cistruct IsStrictOp { 6391cb0ef41Sopenharmony_ci static const bool value = 6401cb0ef41Sopenharmony_ci UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 6411cb0ef41Sopenharmony_ci (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) && 6421cb0ef41Sopenharmony_ci !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) && 6431cb0ef41Sopenharmony_ci !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped); 6441cb0ef41Sopenharmony_ci}; 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci// as_signed<> returns the supplied integral value (or integral castable 6471cb0ef41Sopenharmony_ci// Numeric template) cast as a signed integral of equivalent precision. 6481cb0ef41Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) 6491cb0ef41Sopenharmony_citemplate <typename Src> 6501cb0ef41Sopenharmony_ciconstexpr typename std::make_signed< 6511cb0ef41Sopenharmony_ci typename base::internal::UnderlyingType<Src>::type>::type 6521cb0ef41Sopenharmony_cias_signed(const Src value) { 6531cb0ef41Sopenharmony_ci static_assert(std::is_integral<decltype(as_signed(value))>::value, 6541cb0ef41Sopenharmony_ci "Argument must be a signed or unsigned integer type."); 6551cb0ef41Sopenharmony_ci return static_cast<decltype(as_signed(value))>(value); 6561cb0ef41Sopenharmony_ci} 6571cb0ef41Sopenharmony_ci 6581cb0ef41Sopenharmony_ci// as_unsigned<> returns the supplied integral value (or integral castable 6591cb0ef41Sopenharmony_ci// Numeric template) cast as an unsigned integral of equivalent precision. 6601cb0ef41Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) 6611cb0ef41Sopenharmony_citemplate <typename Src> 6621cb0ef41Sopenharmony_ciconstexpr typename std::make_unsigned< 6631cb0ef41Sopenharmony_ci typename base::internal::UnderlyingType<Src>::type>::type 6641cb0ef41Sopenharmony_cias_unsigned(const Src value) { 6651cb0ef41Sopenharmony_ci static_assert(std::is_integral<decltype(as_unsigned(value))>::value, 6661cb0ef41Sopenharmony_ci "Argument must be a signed or unsigned integer type."); 6671cb0ef41Sopenharmony_ci return static_cast<decltype(as_unsigned(value))>(value); 6681cb0ef41Sopenharmony_ci} 6691cb0ef41Sopenharmony_ci 6701cb0ef41Sopenharmony_citemplate <typename L, typename R> 6711cb0ef41Sopenharmony_ciconstexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range, 6721cb0ef41Sopenharmony_ci const RangeCheck r_range) { 6731cb0ef41Sopenharmony_ci return l_range.IsUnderflow() || r_range.IsOverflow() || 6741cb0ef41Sopenharmony_ci (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) < 6751cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs)); 6761cb0ef41Sopenharmony_ci} 6771cb0ef41Sopenharmony_ci 6781cb0ef41Sopenharmony_citemplate <typename L, typename R> 6791cb0ef41Sopenharmony_cistruct IsLess { 6801cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 6811cb0ef41Sopenharmony_ci "Types must be numeric."); 6821cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 6831cb0ef41Sopenharmony_ci return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 6841cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs)); 6851cb0ef41Sopenharmony_ci } 6861cb0ef41Sopenharmony_ci}; 6871cb0ef41Sopenharmony_ci 6881cb0ef41Sopenharmony_citemplate <typename L, typename R> 6891cb0ef41Sopenharmony_ciconstexpr bool IsLessOrEqualImpl(const L lhs, const R rhs, 6901cb0ef41Sopenharmony_ci const RangeCheck l_range, 6911cb0ef41Sopenharmony_ci const RangeCheck r_range) { 6921cb0ef41Sopenharmony_ci return l_range.IsUnderflow() || r_range.IsOverflow() || 6931cb0ef41Sopenharmony_ci (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <= 6941cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs)); 6951cb0ef41Sopenharmony_ci} 6961cb0ef41Sopenharmony_ci 6971cb0ef41Sopenharmony_citemplate <typename L, typename R> 6981cb0ef41Sopenharmony_cistruct IsLessOrEqual { 6991cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7001cb0ef41Sopenharmony_ci "Types must be numeric."); 7011cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 7021cb0ef41Sopenharmony_ci return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 7031cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs)); 7041cb0ef41Sopenharmony_ci } 7051cb0ef41Sopenharmony_ci}; 7061cb0ef41Sopenharmony_ci 7071cb0ef41Sopenharmony_citemplate <typename L, typename R> 7081cb0ef41Sopenharmony_ciconstexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range, 7091cb0ef41Sopenharmony_ci const RangeCheck r_range) { 7101cb0ef41Sopenharmony_ci return l_range.IsOverflow() || r_range.IsUnderflow() || 7111cb0ef41Sopenharmony_ci (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) > 7121cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs)); 7131cb0ef41Sopenharmony_ci} 7141cb0ef41Sopenharmony_ci 7151cb0ef41Sopenharmony_citemplate <typename L, typename R> 7161cb0ef41Sopenharmony_cistruct IsGreater { 7171cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7181cb0ef41Sopenharmony_ci "Types must be numeric."); 7191cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 7201cb0ef41Sopenharmony_ci return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 7211cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs)); 7221cb0ef41Sopenharmony_ci } 7231cb0ef41Sopenharmony_ci}; 7241cb0ef41Sopenharmony_ci 7251cb0ef41Sopenharmony_citemplate <typename L, typename R> 7261cb0ef41Sopenharmony_ciconstexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs, 7271cb0ef41Sopenharmony_ci const RangeCheck l_range, 7281cb0ef41Sopenharmony_ci const RangeCheck r_range) { 7291cb0ef41Sopenharmony_ci return l_range.IsOverflow() || r_range.IsUnderflow() || 7301cb0ef41Sopenharmony_ci (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >= 7311cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs)); 7321cb0ef41Sopenharmony_ci} 7331cb0ef41Sopenharmony_ci 7341cb0ef41Sopenharmony_citemplate <typename L, typename R> 7351cb0ef41Sopenharmony_cistruct IsGreaterOrEqual { 7361cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7371cb0ef41Sopenharmony_ci "Types must be numeric."); 7381cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 7391cb0ef41Sopenharmony_ci return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 7401cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs)); 7411cb0ef41Sopenharmony_ci } 7421cb0ef41Sopenharmony_ci}; 7431cb0ef41Sopenharmony_ci 7441cb0ef41Sopenharmony_citemplate <typename L, typename R> 7451cb0ef41Sopenharmony_cistruct IsEqual { 7461cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7471cb0ef41Sopenharmony_ci "Types must be numeric."); 7481cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 7491cb0ef41Sopenharmony_ci return DstRangeRelationToSrcRange<R>(lhs) == 7501cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs) && 7511cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(lhs) == 7521cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs); 7531cb0ef41Sopenharmony_ci } 7541cb0ef41Sopenharmony_ci}; 7551cb0ef41Sopenharmony_ci 7561cb0ef41Sopenharmony_citemplate <typename L, typename R> 7571cb0ef41Sopenharmony_cistruct IsNotEqual { 7581cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7591cb0ef41Sopenharmony_ci "Types must be numeric."); 7601cb0ef41Sopenharmony_ci static constexpr bool Test(const L lhs, const R rhs) { 7611cb0ef41Sopenharmony_ci return DstRangeRelationToSrcRange<R>(lhs) != 7621cb0ef41Sopenharmony_ci DstRangeRelationToSrcRange<L>(rhs) || 7631cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(lhs) != 7641cb0ef41Sopenharmony_ci static_cast<decltype(lhs + rhs)>(rhs); 7651cb0ef41Sopenharmony_ci } 7661cb0ef41Sopenharmony_ci}; 7671cb0ef41Sopenharmony_ci 7681cb0ef41Sopenharmony_ci// These perform the actual math operations on the CheckedNumerics. 7691cb0ef41Sopenharmony_ci// Binary arithmetic operations. 7701cb0ef41Sopenharmony_citemplate <template <typename, typename> class C, typename L, typename R> 7711cb0ef41Sopenharmony_ciconstexpr bool SafeCompare(const L lhs, const R rhs) { 7721cb0ef41Sopenharmony_ci static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 7731cb0ef41Sopenharmony_ci "Types must be numeric."); 7741cb0ef41Sopenharmony_ci using Promotion = BigEnoughPromotion<L, R>; 7751cb0ef41Sopenharmony_ci using BigType = typename Promotion::type; 7761cb0ef41Sopenharmony_ci return Promotion::is_contained 7771cb0ef41Sopenharmony_ci // Force to a larger type for speed if both are contained. 7781cb0ef41Sopenharmony_ci ? C<BigType, BigType>::Test( 7791cb0ef41Sopenharmony_ci static_cast<BigType>(static_cast<L>(lhs)), 7801cb0ef41Sopenharmony_ci static_cast<BigType>(static_cast<R>(rhs))) 7811cb0ef41Sopenharmony_ci // Let the template functions figure it out for mixed types. 7821cb0ef41Sopenharmony_ci : C<L, R>::Test(lhs, rhs); 7831cb0ef41Sopenharmony_ci} 7841cb0ef41Sopenharmony_ci 7851cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 7861cb0ef41Sopenharmony_ciconstexpr bool IsMaxInRangeForNumericType() { 7871cb0ef41Sopenharmony_ci return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), 7881cb0ef41Sopenharmony_ci std::numeric_limits<Src>::max()); 7891cb0ef41Sopenharmony_ci} 7901cb0ef41Sopenharmony_ci 7911cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 7921cb0ef41Sopenharmony_ciconstexpr bool IsMinInRangeForNumericType() { 7931cb0ef41Sopenharmony_ci return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), 7941cb0ef41Sopenharmony_ci std::numeric_limits<Src>::lowest()); 7951cb0ef41Sopenharmony_ci} 7961cb0ef41Sopenharmony_ci 7971cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 7981cb0ef41Sopenharmony_ciconstexpr Dst CommonMax() { 7991cb0ef41Sopenharmony_ci return !IsMaxInRangeForNumericType<Dst, Src>() 8001cb0ef41Sopenharmony_ci ? Dst(std::numeric_limits<Dst>::max()) 8011cb0ef41Sopenharmony_ci : Dst(std::numeric_limits<Src>::max()); 8021cb0ef41Sopenharmony_ci} 8031cb0ef41Sopenharmony_ci 8041cb0ef41Sopenharmony_citemplate <typename Dst, typename Src> 8051cb0ef41Sopenharmony_ciconstexpr Dst CommonMin() { 8061cb0ef41Sopenharmony_ci return !IsMinInRangeForNumericType<Dst, Src>() 8071cb0ef41Sopenharmony_ci ? Dst(std::numeric_limits<Dst>::lowest()) 8081cb0ef41Sopenharmony_ci : Dst(std::numeric_limits<Src>::lowest()); 8091cb0ef41Sopenharmony_ci} 8101cb0ef41Sopenharmony_ci 8111cb0ef41Sopenharmony_ci// This is a wrapper to generate return the max or min for a supplied type. 8121cb0ef41Sopenharmony_ci// If the argument is false, the returned value is the maximum. If true the 8131cb0ef41Sopenharmony_ci// returned value is the minimum. 8141cb0ef41Sopenharmony_citemplate <typename Dst, typename Src = Dst> 8151cb0ef41Sopenharmony_ciconstexpr Dst CommonMaxOrMin(bool is_min) { 8161cb0ef41Sopenharmony_ci return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>(); 8171cb0ef41Sopenharmony_ci} 8181cb0ef41Sopenharmony_ci 8191cb0ef41Sopenharmony_ci} // namespace internal 8201cb0ef41Sopenharmony_ci} // namespace base 8211cb0ef41Sopenharmony_ci} // namespace v8 8221cb0ef41Sopenharmony_ci 8231cb0ef41Sopenharmony_ci#endif // V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 824