Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,851 @@
|
|||
// 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue