You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
734 lines
30 KiB
734 lines
30 KiB
// 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 PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
|
#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
|
|
|
#include <stdint.h> |
|
|
|
#include <limits> |
|
#include <type_traits> |
|
|
|
namespace pdfium { |
|
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) ? 0 - static_cast<UnsignedT>(value) |
|
: static_cast<UnsignedT>(value); |
|
} |
|
|
|
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())); |
|
} |
|
}; |
|
|
|
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; |
|
}; |
|
|
|
// This hacks around libstdc++ 4.6 missing stuff in type_traits. |
|
#if defined(__GLIBCXX__) |
|
#define PRIV_GLIBCXX_4_7_0 20120322 |
|
#define PRIV_GLIBCXX_4_5_4 20120702 |
|
#define PRIV_GLIBCXX_4_6_4 20121127 |
|
#if (__GLIBCXX__ < PRIV_GLIBCXX_4_7_0 || __GLIBCXX__ == PRIV_GLIBCXX_4_5_4 || \ |
|
__GLIBCXX__ == PRIV_GLIBCXX_4_6_4) |
|
#define PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
|
#undef PRIV_GLIBCXX_4_7_0 |
|
#undef PRIV_GLIBCXX_4_5_4 |
|
#undef PRIV_GLIBCXX_4_6_4 |
|
#endif |
|
#endif |
|
|
|
// 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> { |
|
#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
|
using type = __underlying_type(T); |
|
#else |
|
using type = typename std::underlying_type<T>::type; |
|
#endif |
|
static const bool value = std::is_arithmetic<type>::value; |
|
}; |
|
|
|
#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
|
#undef PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
|
#endif |
|
|
|
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 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_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_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_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 IsStrictOp { |
|
static const bool value = |
|
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
|
(UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); |
|
}; |
|
|
|
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); |
|
}; |
|
|
|
} // namespace internal |
|
} // namespace base |
|
} // namespace pdfium |
|
|
|
#endif // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
|
|