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