| Index: third_party/base/numerics/safe_conversions_impl.h
|
| diff --git a/third_party/base/numerics/safe_conversions_impl.h b/third_party/base/numerics/safe_conversions_impl.h
|
| index e1c4c3b75635568c1838a76e802e6f0f94dce8d5..2a7ce146e3c2b5b5fd4324cbb07e27839723f8ef 100644
|
| --- a/third_party/base/numerics/safe_conversions_impl.h
|
| +++ b/third_party/base/numerics/safe_conversions_impl.h
|
| @@ -2,29 +2,81 @@
|
| // 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_SAFE_CONVERSIONS_IMPL_H_
|
| -#define PDFIUM_THIRD_PARTY_BASE_SAFE_CONVERSIONS_IMPL_H_
|
| +#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
| +#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
|
|
| -#include <assert.h>
|
| -#include <limits>
|
| +#include <stdint.h>
|
|
|
| -#include "third_party/base/macros.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 one by adding one to the number of non-sign bits. This allows
|
| -// for accurate range comparisons between floating point and integer types.
|
| +// we can compute an analog using std::numeric_limits<>::digits.
|
| template <typename NumericType>
|
| struct MaxExponent {
|
| - static const int value = std::numeric_limits<NumericType>::is_iec559
|
| + static const int value = std::is_floating_point<NumericType>::value
|
| ? std::numeric_limits<NumericType>::max_exponent
|
| - : (sizeof(NumericType) * 8 + 1 -
|
| - std::numeric_limits<NumericType>::is_signed);
|
| + : 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
|
| @@ -32,7 +84,7 @@ enum IntegerRepresentation {
|
|
|
| // 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>::min() >= numeric_limits<Dst>::min() are true.
|
| +// 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 {
|
| @@ -43,16 +95,14 @@ enum NumericRangeRepresentation {
|
| // 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::numeric_limits<Dst>::is_signed
|
| - ? INTEGER_REPRESENTATION_SIGNED
|
| - : INTEGER_REPRESENTATION_UNSIGNED,
|
| - IntegerRepresentation SrcSign =
|
| - std::numeric_limits<Src>::is_signed
|
| - ? INTEGER_REPRESENTATION_SIGNED
|
| - : INTEGER_REPRESENTATION_UNSIGNED >
|
| +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
|
| @@ -87,132 +137,598 @@ struct StaticDstRangeRelationToSrcRange<Dst,
|
| static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
|
| };
|
|
|
| -enum RangeConstraint {
|
| - RANGE_VALID = 0x0, // Value can be represented by the destination type.
|
| - RANGE_UNDERFLOW = 0x1, // Value would overflow.
|
| - RANGE_OVERFLOW = 0x2, // Value would underflow.
|
| - RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
|
| +// 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_;
|
| };
|
|
|
| -// Helper function for coercing an int back to a RangeContraint.
|
| -inline RangeConstraint GetRangeConstraint(int integer_range_constraint) {
|
| - assert(integer_range_constraint >= RANGE_VALID &&
|
| - integer_range_constraint <= RANGE_INVALID);
|
| - return static_cast<RangeConstraint>(integer_range_constraint);
|
| -}
|
| +// 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>;
|
|
|
| -// This function creates a RangeConstraint from an upper and lower bound
|
| -// check by taking advantage of the fact that only NaN can be out of range in
|
| -// both directions at once.
|
| -inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
|
| - bool is_in_lower_bound) {
|
| - return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
|
| - (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
|
| -}
|
| + // 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>
|
|
|
| -template <
|
| - typename Dst,
|
| - typename Src,
|
| - IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
|
| - ? INTEGER_REPRESENTATION_SIGNED
|
| - : INTEGER_REPRESENTATION_UNSIGNED,
|
| - IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
|
| - ? INTEGER_REPRESENTATION_SIGNED
|
| - : INTEGER_REPRESENTATION_UNSIGNED,
|
| - NumericRangeRepresentation DstRange =
|
| - StaticDstRangeRelationToSrcRange<Dst, Src>::value >
|
| + // 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.
|
|
|
| -// Dst range is statically determined to contain Src: Nothing to check.
|
| +// 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 RangeConstraint Check(Src value) { return RANGE_VALID; }
|
| + 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.
|
| -template <typename Dst, typename Src>
|
| +// 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 RangeConstraint Check(Src value) {
|
| - return std::numeric_limits<Dst>::is_iec559
|
| - ? GetRangeConstraint(value <= std::numeric_limits<Dst>::max(),
|
| - value >= -std::numeric_limits<Dst>::max())
|
| - : GetRangeConstraint(value <= std::numeric_limits<Dst>::max(),
|
| - value >= std::numeric_limits<Dst>::min());
|
| + 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 boundary can be exceeded.
|
| -template <typename Dst, typename Src>
|
| +// 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 RangeConstraint Check(Src value) {
|
| - return GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), true);
|
| + 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: The upper boundary may be exceeded.
|
| -template <typename Dst, typename Src>
|
| +// 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 RangeConstraint Check(Src value) {
|
| - return sizeof(Dst) > sizeof(Src)
|
| - ? RANGE_VALID
|
| - : GetRangeConstraint(
|
| - value <= static_cast<Src>(std::numeric_limits<Dst>::max()),
|
| - true);
|
| + 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.
|
| -template <typename Dst, typename Src>
|
| +// 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 RangeConstraint Check(Src value) {
|
| - return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
|
| - ? GetRangeConstraint(true, value >= static_cast<Src>(0))
|
| - : GetRangeConstraint(
|
| - value <= static_cast<Src>(std::numeric_limits<Dst>::max()),
|
| - value >= static_cast<Src>(0));
|
| + 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, typename Src>
|
| -inline RangeConstraint DstRangeRelationToSrcRange(Src value) {
|
| - COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized,
|
| - argument_must_be_numeric);
|
| - COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized,
|
| - result_must_be_numeric);
|
| - return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
|
| +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_SAFE_CONVERSIONS_IMPL_H_
|
| +#endif // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
|
|