123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
- #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
- #include <stdint.h>
- #include <limits>
- #include <type_traits>
- #if defined(__GNUC__) || defined(__clang__)
- #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
- #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
- #else
- #define BASE_NUMERICS_LIKELY(x) (x)
- #define BASE_NUMERICS_UNLIKELY(x) (x)
- #endif
- namespace base {
- namespace internal {
- // The std library doesn't provide a binary max_exponent for integers, however
- // we can compute an analog using std::numeric_limits<>::digits.
- template <typename NumericType>
- struct MaxExponent {
- static const int value = std::is_floating_point<NumericType>::value
- ? std::numeric_limits<NumericType>::max_exponent
- : std::numeric_limits<NumericType>::digits + 1;
- };
- // The number of bits (including the sign) in an integer. Eliminates sizeof
- // hacks.
- template <typename NumericType>
- struct IntegerBitsPlusSign {
- static const int value = std::numeric_limits<NumericType>::digits +
- std::is_signed<NumericType>::value;
- };
- // Helper templates for integer manipulations.
- template <typename Integer>
- struct PositionOfSignBit {
- static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
- };
- // Determines if a numeric value is negative without throwing compiler
- // warnings on: unsigned(value) < 0.
- template <typename T,
- typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
- constexpr bool IsValueNegative(T value) {
- static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
- return value < 0;
- }
- template <typename T,
- typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
- constexpr bool IsValueNegative(T) {
- static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
- return false;
- }
- // This performs a fast negation, returning a signed value. It works on unsigned
- // arguments, but probably doesn't do what you want for any unsigned value
- // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
- template <typename T>
- constexpr typename std::make_signed<T>::type ConditionalNegate(
- T x,
- bool is_negative) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- using SignedT = typename std::make_signed<T>::type;
- using UnsignedT = typename std::make_unsigned<T>::type;
- return static_cast<SignedT>(
- (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
- }
- // This performs a safe, absolute value via unsigned overflow.
- template <typename T>
- constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
- static_assert(std::is_integral<T>::value, "Type must be integral");
- using UnsignedT = typename std::make_unsigned<T>::type;
- return IsValueNegative(value)
- ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
- : static_cast<UnsignedT>(value);
- }
- // This allows us to switch paths on known compile-time constants.
- #if defined(__clang__) || defined(__GNUC__)
- constexpr bool CanDetectCompileTimeConstant() {
- return true;
- }
- template <typename T>
- constexpr bool IsCompileTimeConstant(const T v) {
- return __builtin_constant_p(v);
- }
- #else
- constexpr bool CanDetectCompileTimeConstant() {
- return false;
- }
- template <typename T>
- constexpr bool IsCompileTimeConstant(const T) {
- return false;
- }
- #endif
- template <typename T>
- constexpr bool MustTreatAsConstexpr(const T v) {
- // Either we can't detect a compile-time constant, and must always use the
- // constexpr path, or we know we have a compile-time constant.
- return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
- }
- // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
- // Also used in a constexpr template to trigger a compilation failure on
- // an error condition.
- struct CheckOnFailure {
- template <typename T>
- static T HandleFailure() {
- #if defined(_MSC_VER)
- __debugbreak();
- #elif defined(__GNUC__) || defined(__clang__)
- __builtin_trap();
- #else
- ((void)(*(volatile char*)0 = 0));
- #endif
- return T();
- }
- };
- enum IntegerRepresentation {
- INTEGER_REPRESENTATION_UNSIGNED,
- INTEGER_REPRESENTATION_SIGNED
- };
- // A range for a given nunmeric Src type is contained for a given numeric Dst
- // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
- // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
- // We implement this as template specializations rather than simple static
- // comparisons to ensure type correctness in our comparisons.
- enum NumericRangeRepresentation {
- NUMERIC_RANGE_NOT_CONTAINED,
- NUMERIC_RANGE_CONTAINED
- };
- // Helper templates to statically determine if our destination type can contain
- // maximum and minimum values represented by the source type.
- template <typename Dst,
- typename Src,
- IntegerRepresentation DstSign = std::is_signed<Dst>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- IntegerRepresentation SrcSign = std::is_signed<Src>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED>
- struct StaticDstRangeRelationToSrcRange;
- // Same sign: Dst is guaranteed to contain Src only if its range is equal or
- // larger.
- template <typename Dst, typename Src, IntegerRepresentation Sign>
- struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
- static const NumericRangeRepresentation value =
- MaxExponent<Dst>::value >= MaxExponent<Src>::value
- ? NUMERIC_RANGE_CONTAINED
- : NUMERIC_RANGE_NOT_CONTAINED;
- };
- // Unsigned to signed: Dst is guaranteed to contain source only if its range is
- // larger.
- template <typename Dst, typename Src>
- struct StaticDstRangeRelationToSrcRange<Dst,
- Src,
- INTEGER_REPRESENTATION_SIGNED,
- INTEGER_REPRESENTATION_UNSIGNED> {
- static const NumericRangeRepresentation value =
- MaxExponent<Dst>::value > MaxExponent<Src>::value
- ? NUMERIC_RANGE_CONTAINED
- : NUMERIC_RANGE_NOT_CONTAINED;
- };
- // Signed to unsigned: Dst cannot be statically determined to contain Src.
- template <typename Dst, typename Src>
- struct StaticDstRangeRelationToSrcRange<Dst,
- Src,
- INTEGER_REPRESENTATION_UNSIGNED,
- INTEGER_REPRESENTATION_SIGNED> {
- static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
- };
- // This class wraps the range constraints as separate booleans so the compiler
- // can identify constants and eliminate unused code paths.
- class RangeCheck {
- public:
- constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
- : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
- constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
- constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
- constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
- constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
- constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
- constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
- constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
- constexpr bool operator==(const RangeCheck rhs) const {
- return is_underflow_ == rhs.is_underflow_ &&
- is_overflow_ == rhs.is_overflow_;
- }
- constexpr bool operator!=(const RangeCheck rhs) const {
- return !(*this == rhs);
- }
- private:
- // Do not change the order of these member variables. The integral conversion
- // optimization depends on this exact order.
- const bool is_underflow_;
- const bool is_overflow_;
- };
- // The following helper template addresses a corner case in range checks for
- // conversion from a floating-point type to an integral type of smaller range
- // but larger precision (e.g. float -> unsigned). The problem is as follows:
- // 1. Integral maximum is always one less than a power of two, so it must be
- // truncated to fit the mantissa of the floating point. The direction of
- // rounding is implementation defined, but by default it's always IEEE
- // floats, which round to nearest and thus result in a value of larger
- // magnitude than the integral value.
- // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
- // // is 4294967295u.
- // 2. If the floating point value is equal to the promoted integral maximum
- // value, a range check will erroneously pass.
- // Example: (4294967296f <= 4294967295u) // This is true due to a precision
- // // loss in rounding up to float.
- // 3. When the floating point value is then converted to an integral, the
- // resulting value is out of range for the target integral type and
- // thus is implementation defined.
- // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
- // To fix this bug we manually truncate the maximum value when the destination
- // type is an integral of larger precision than the source floating-point type,
- // such that the resulting maximum is represented exactly as a floating point.
- template <typename Dst, typename Src, template <typename> class Bounds>
- struct NarrowingRange {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = typename std::numeric_limits<Dst>;
- // Computes the mask required to make an accurate comparison between types.
- static const int kShift =
- (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
- SrcLimits::digits < DstLimits::digits)
- ? (DstLimits::digits - SrcLimits::digits)
- : 0;
- template <
- typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
- // Masks out the integer bits that are beyond the precision of the
- // intermediate type used for comparison.
- static constexpr T Adjust(T value) {
- static_assert(std::is_same<T, Dst>::value, "");
- static_assert(kShift < DstLimits::digits, "");
- return static_cast<T>(
- ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
- IsValueNegative(value)));
- }
- template <typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* =
- nullptr>
- static constexpr T Adjust(T value) {
- static_assert(std::is_same<T, Dst>::value, "");
- static_assert(kShift == 0, "");
- return value;
- }
- static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
- static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
- };
- template <typename Dst,
- typename Src,
- template <typename> class Bounds,
- IntegerRepresentation DstSign = std::is_signed<Dst>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- IntegerRepresentation SrcSign = std::is_signed<Src>::value
- ? INTEGER_REPRESENTATION_SIGNED
- : INTEGER_REPRESENTATION_UNSIGNED,
- NumericRangeRepresentation DstRange =
- StaticDstRangeRelationToSrcRange<Dst, Src>::value>
- struct DstRangeRelationToSrcRangeImpl;
- // The following templates are for ranges that must be verified at runtime. We
- // split it into checks based on signedness to avoid confusing casts and
- // compiler warnings on signed an unsigned comparisons.
- // Same sign narrowing: The range is contained for normal limits.
- template <typename Dst,
- typename Src,
- template <typename> class Bounds,
- IntegerRepresentation DstSign,
- IntegerRepresentation SrcSign>
- struct DstRangeRelationToSrcRangeImpl<Dst,
- Src,
- Bounds,
- DstSign,
- SrcSign,
- NUMERIC_RANGE_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(
- static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
- static_cast<Dst>(value) >= DstLimits::lowest(),
- static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
- static_cast<Dst>(value) <= DstLimits::max());
- }
- };
- // Signed to signed narrowing: Both the upper and lower boundaries may be
- // exceeded for standard limits.
- template <typename Dst, typename Src, template <typename> class Bounds>
- struct DstRangeRelationToSrcRangeImpl<Dst,
- Src,
- Bounds,
- INTEGER_REPRESENTATION_SIGNED,
- INTEGER_REPRESENTATION_SIGNED,
- NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
- }
- };
- // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
- // standard limits.
- template <typename Dst, typename Src, template <typename> class Bounds>
- struct DstRangeRelationToSrcRangeImpl<Dst,
- Src,
- Bounds,
- INTEGER_REPRESENTATION_UNSIGNED,
- INTEGER_REPRESENTATION_UNSIGNED,
- NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- return RangeCheck(
- DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
- value <= DstLimits::max());
- }
- };
- // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
- template <typename Dst, typename Src, template <typename> class Bounds>
- struct DstRangeRelationToSrcRangeImpl<Dst,
- Src,
- Bounds,
- INTEGER_REPRESENTATION_SIGNED,
- INTEGER_REPRESENTATION_UNSIGNED,
- NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- using Promotion = decltype(Src() + Dst());
- return RangeCheck(DstLimits::lowest() <= Dst(0) ||
- static_cast<Promotion>(value) >=
- static_cast<Promotion>(DstLimits::lowest()),
- static_cast<Promotion>(value) <=
- static_cast<Promotion>(DstLimits::max()));
- }
- };
- // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
- // and any negative value exceeds the lower boundary for standard limits.
- template <typename Dst, typename Src, template <typename> class Bounds>
- struct DstRangeRelationToSrcRangeImpl<Dst,
- Src,
- Bounds,
- INTEGER_REPRESENTATION_UNSIGNED,
- INTEGER_REPRESENTATION_SIGNED,
- NUMERIC_RANGE_NOT_CONTAINED> {
- static constexpr RangeCheck Check(Src value) {
- using SrcLimits = std::numeric_limits<Src>;
- using DstLimits = NarrowingRange<Dst, Src, Bounds>;
- using Promotion = decltype(Src() + Dst());
- return RangeCheck(
- value >= Src(0) && (DstLimits::lowest() == 0 ||
- static_cast<Dst>(value) >= DstLimits::lowest()),
- static_cast<Promotion>(SrcLimits::max()) <=
- static_cast<Promotion>(DstLimits::max()) ||
- static_cast<Promotion>(value) <=
- static_cast<Promotion>(DstLimits::max()));
- }
- };
- // Simple wrapper for statically checking if a type's range is contained.
- template <typename Dst, typename Src>
- struct IsTypeInRangeForNumericType {
- static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
- NUMERIC_RANGE_CONTAINED;
- };
- template <typename Dst,
- template <typename> class Bounds = std::numeric_limits,
- typename Src>
- constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
- static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
- static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
- return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
- }
- // Integer promotion templates used by the portable checked integer arithmetic.
- template <size_t Size, bool IsSigned>
- struct IntegerForDigitsAndSign;
- #define INTEGER_FOR_DIGITS_AND_SIGN(I) \
- template <> \
- struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
- std::is_signed<I>::value> { \
- using type = I; \
- }
- INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
- INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
- INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
- INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
- INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
- INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
- INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
- INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
- #undef INTEGER_FOR_DIGITS_AND_SIGN
- // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
- // support 128-bit math, then the ArithmeticPromotion template below will need
- // to be updated (or more likely replaced with a decltype expression).
- static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
- "Max integer size not supported for this toolchain.");
- template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
- struct TwiceWiderInteger {
- using type =
- typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
- IsSigned>::type;
- };
- enum ArithmeticPromotionCategory {
- LEFT_PROMOTION, // Use the type of the left-hand argument.
- RIGHT_PROMOTION // Use the type of the right-hand argument.
- };
- // Determines the type that can represent the largest positive value.
- template <typename Lhs,
- typename Rhs,
- ArithmeticPromotionCategory Promotion =
- (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION>
- struct MaxExponentPromotion;
- template <typename Lhs, typename Rhs>
- struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
- using type = Lhs;
- };
- template <typename Lhs, typename Rhs>
- struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
- using type = Rhs;
- };
- // Determines the type that can represent the lowest arithmetic value.
- template <typename Lhs,
- typename Rhs,
- ArithmeticPromotionCategory Promotion =
- std::is_signed<Lhs>::value
- ? (std::is_signed<Rhs>::value
- ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION)
- : LEFT_PROMOTION)
- : (std::is_signed<Rhs>::value
- ? RIGHT_PROMOTION
- : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION))>
- struct LowestValuePromotion;
- template <typename Lhs, typename Rhs>
- struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
- using type = Lhs;
- };
- template <typename Lhs, typename Rhs>
- struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
- using type = Rhs;
- };
- // Determines the type that is best able to represent an arithmetic result.
- template <
- typename Lhs,
- typename Rhs = Lhs,
- bool is_intmax_type =
- std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
- IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
- value == IntegerBitsPlusSign<intmax_t>::value,
- bool is_max_exponent =
- StaticDstRangeRelationToSrcRange<
- typename MaxExponentPromotion<Lhs, Rhs>::type,
- Lhs>::value ==
- NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
- typename MaxExponentPromotion<Lhs, Rhs>::type,
- Rhs>::value == NUMERIC_RANGE_CONTAINED>
- struct BigEnoughPromotion;
- // The side with the max exponent is big enough.
- template <typename Lhs, typename Rhs, bool is_intmax_type>
- struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
- using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
- static const bool is_contained = true;
- };
- // We can use a twice wider type to fit.
- template <typename Lhs, typename Rhs>
- struct BigEnoughPromotion<Lhs, Rhs, false, false> {
- using type =
- typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
- std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value>::type;
- static const bool is_contained = true;
- };
- // No type is large enough.
- template <typename Lhs, typename Rhs>
- struct BigEnoughPromotion<Lhs, Rhs, true, false> {
- using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
- static const bool is_contained = false;
- };
- // We can statically check if operations on the provided types can wrap, so we
- // can skip the checked operations if they're not needed. So, for an integer we
- // care if the destination type preserves the sign and is twice the width of
- // the source.
- template <typename T, typename Lhs, typename Rhs = Lhs>
- struct IsIntegerArithmeticSafe {
- static const bool value =
- !std::is_floating_point<T>::value &&
- !std::is_floating_point<Lhs>::value &&
- !std::is_floating_point<Rhs>::value &&
- std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
- IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
- std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
- IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
- };
- // Promotes to a type that can represent any possible result of a binary
- // arithmetic operation with the source types.
- template <typename Lhs,
- typename Rhs,
- bool is_promotion_possible = IsIntegerArithmeticSafe<
- typename std::conditional<std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value,
- intmax_t,
- uintmax_t>::type,
- typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
- struct FastIntegerArithmeticPromotion;
- template <typename Lhs, typename Rhs>
- struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
- using type =
- typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
- std::is_signed<Lhs>::value ||
- std::is_signed<Rhs>::value>::type;
- static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
- static const bool is_contained = true;
- };
- template <typename Lhs, typename Rhs>
- struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
- using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
- static const bool is_contained = false;
- };
- // Extracts the underlying type from an enum.
- template <typename T, bool is_enum = std::is_enum<T>::value>
- struct ArithmeticOrUnderlyingEnum;
- template <typename T>
- struct ArithmeticOrUnderlyingEnum<T, true> {
- using type = typename std::underlying_type<T>::type;
- static const bool value = std::is_arithmetic<type>::value;
- };
- template <typename T>
- struct ArithmeticOrUnderlyingEnum<T, false> {
- using type = T;
- static const bool value = std::is_arithmetic<type>::value;
- };
- // The following are helper templates used in the CheckedNumeric class.
- template <typename T>
- class CheckedNumeric;
- template <typename T>
- class ClampedNumeric;
- template <typename T>
- class StrictNumeric;
- // Used to treat CheckedNumeric and arithmetic underlying types the same.
- template <typename T>
- struct UnderlyingType {
- using type = typename ArithmeticOrUnderlyingEnum<T>::type;
- static const bool is_numeric = std::is_arithmetic<type>::value;
- static const bool is_checked = false;
- static const bool is_clamped = false;
- static const bool is_strict = false;
- };
- template <typename T>
- struct UnderlyingType<CheckedNumeric<T>> {
- using type = T;
- static const bool is_numeric = true;
- static const bool is_checked = true;
- static const bool is_clamped = false;
- static const bool is_strict = false;
- };
- template <typename T>
- struct UnderlyingType<ClampedNumeric<T>> {
- using type = T;
- static const bool is_numeric = true;
- static const bool is_checked = false;
- static const bool is_clamped = true;
- static const bool is_strict = false;
- };
- template <typename T>
- struct UnderlyingType<StrictNumeric<T>> {
- using type = T;
- static const bool is_numeric = true;
- static const bool is_checked = false;
- static const bool is_clamped = false;
- static const bool is_strict = true;
- };
- template <typename L, typename R>
- struct IsCheckedOp {
- static const bool value =
- UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
- };
- template <typename L, typename R>
- struct IsClampedOp {
- static const bool value =
- UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
- !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
- };
- template <typename L, typename R>
- struct IsStrictOp {
- static const bool value =
- UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
- !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
- !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
- };
- // as_signed<> returns the supplied integral value (or integral castable
- // Numeric template) cast as a signed integral of equivalent precision.
- // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
- template <typename Src>
- constexpr typename std::make_signed<
- typename base::internal::UnderlyingType<Src>::type>::type
- as_signed(const Src value) {
- static_assert(std::is_integral<decltype(as_signed(value))>::value,
- "Argument must be a signed or unsigned integer type.");
- return static_cast<decltype(as_signed(value))>(value);
- }
- // as_unsigned<> returns the supplied integral value (or integral castable
- // Numeric template) cast as an unsigned integral of equivalent precision.
- // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
- template <typename Src>
- constexpr typename std::make_unsigned<
- typename base::internal::UnderlyingType<Src>::type>::type
- as_unsigned(const Src value) {
- static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
- "Argument must be a signed or unsigned integer type.");
- return static_cast<decltype(as_unsigned(value))>(value);
- }
- template <typename L, typename R>
- constexpr bool IsLessImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsUnderflow() || r_range.IsOverflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) <
- static_cast<decltype(lhs + rhs)>(rhs));
- }
- template <typename L, typename R>
- struct IsLess {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
- };
- template <typename L, typename R>
- constexpr bool IsLessOrEqualImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsUnderflow() || r_range.IsOverflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) <=
- static_cast<decltype(lhs + rhs)>(rhs));
- }
- template <typename L, typename R>
- struct IsLessOrEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
- };
- template <typename L, typename R>
- constexpr bool IsGreaterImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsOverflow() || r_range.IsUnderflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) >
- static_cast<decltype(lhs + rhs)>(rhs));
- }
- template <typename L, typename R>
- struct IsGreater {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
- };
- template <typename L, typename R>
- constexpr bool IsGreaterOrEqualImpl(const L lhs,
- const R rhs,
- const RangeCheck l_range,
- const RangeCheck r_range) {
- return l_range.IsOverflow() || r_range.IsUnderflow() ||
- (l_range == r_range &&
- static_cast<decltype(lhs + rhs)>(lhs) >=
- static_cast<decltype(lhs + rhs)>(rhs));
- }
- template <typename L, typename R>
- struct IsGreaterOrEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
- DstRangeRelationToSrcRange<L>(rhs));
- }
- };
- template <typename L, typename R>
- struct IsEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return DstRangeRelationToSrcRange<R>(lhs) ==
- DstRangeRelationToSrcRange<L>(rhs) &&
- static_cast<decltype(lhs + rhs)>(lhs) ==
- static_cast<decltype(lhs + rhs)>(rhs);
- }
- };
- template <typename L, typename R>
- struct IsNotEqual {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- static constexpr bool Test(const L lhs, const R rhs) {
- return DstRangeRelationToSrcRange<R>(lhs) !=
- DstRangeRelationToSrcRange<L>(rhs) ||
- static_cast<decltype(lhs + rhs)>(lhs) !=
- static_cast<decltype(lhs + rhs)>(rhs);
- }
- };
- // These perform the actual math operations on the CheckedNumerics.
- // Binary arithmetic operations.
- template <template <typename, typename> class C, typename L, typename R>
- constexpr bool SafeCompare(const L lhs, const R rhs) {
- static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
- "Types must be numeric.");
- using Promotion = BigEnoughPromotion<L, R>;
- using BigType = typename Promotion::type;
- return Promotion::is_contained
- // Force to a larger type for speed if both are contained.
- ? C<BigType, BigType>::Test(
- static_cast<BigType>(static_cast<L>(lhs)),
- static_cast<BigType>(static_cast<R>(rhs)))
- // Let the template functions figure it out for mixed types.
- : C<L, R>::Test(lhs, rhs);
- }
- template <typename Dst, typename Src>
- constexpr bool IsMaxInRangeForNumericType() {
- return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
- std::numeric_limits<Src>::max());
- }
- template <typename Dst, typename Src>
- constexpr bool IsMinInRangeForNumericType() {
- return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
- std::numeric_limits<Src>::lowest());
- }
- template <typename Dst, typename Src>
- constexpr Dst CommonMax() {
- return !IsMaxInRangeForNumericType<Dst, Src>()
- ? Dst(std::numeric_limits<Dst>::max())
- : Dst(std::numeric_limits<Src>::max());
- }
- template <typename Dst, typename Src>
- constexpr Dst CommonMin() {
- return !IsMinInRangeForNumericType<Dst, Src>()
- ? Dst(std::numeric_limits<Dst>::lowest())
- : Dst(std::numeric_limits<Src>::lowest());
- }
- // This is a wrapper to generate return the max or min for a supplied type.
- // If the argument is false, the returned value is the maximum. If true the
- // returned value is the minimum.
- template <typename Dst, typename Src = Dst>
- constexpr Dst CommonMaxOrMin(bool is_min) {
- return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
- }
- } // namespace internal
- } // namespace base
- #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|