Index: base/numerics/safe_conversions_impl.h |
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h |
index 5ef3321474e5d2ac83777dc1f3210cf7f0af59ad..270d5916b983b102a9247c51100a3ad1f85f78d5 100644 |
--- a/base/numerics/safe_conversions_impl.h |
+++ b/base/numerics/safe_conversions_impl.h |
@@ -167,28 +167,37 @@ struct StaticDstRangeRelationToSrcRange<Dst, |
}; |
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_VALID = 0x0, // Value can be represented by the destination type. |
+ RANGE_UNDERFLOW = 0x1, // Value would underflow. |
+ RANGE_OVERFLOW = 0x2, // Value would overflow. |
RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). |
}; |
-// Helper function for coercing an int back to a RangeContraint. |
-constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) { |
- // TODO(jschuh): Once we get full C++14 support we want this |
- // assert(integer_range_constraint >= RANGE_VALID && |
- // integer_range_constraint <= RANGE_INVALID) |
- return static_cast<RangeConstraint>(integer_range_constraint); |
-} |
+// 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_upper_bound, bool is_in_lower_bound) |
+ : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {} |
+ constexpr RangeCheck() : is_overflow_(0), is_underflow_(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_; } |
+ |
+ // These are some wrappers to make the tests a bit cleaner. |
+ constexpr operator RangeConstraint() const { |
+ return static_cast<RangeConstraint>(static_cast<int>(is_overflow_) << 1 | |
+ static_cast<int>(is_underflow_)); |
+ } |
+ constexpr bool operator==(const RangeConstraint rhs) const { |
+ return rhs == static_cast<RangeConstraint>(*this); |
+ } |
-// 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. |
-constexpr 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)); |
-} |
+ private: |
+ const bool is_overflow_; |
+ const bool is_underflow_; |
+}; |
// 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 |
@@ -211,11 +220,9 @@ constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, |
// 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 = std::numeric_limits> |
+template <typename Dst, typename Src, template <typename> class Bounds> |
struct NarrowingRange { |
- using SrcLimits = typename std::numeric_limits<Src>; |
+ using SrcLimits = std::numeric_limits<Src>; |
using DstLimits = typename std::numeric_limits<Dst>; |
// Computes the mask required to make an accurate comparison between types. |
@@ -253,6 +260,7 @@ struct NarrowingRange { |
template <typename Dst, |
typename Src, |
+ template <typename> class Bounds, |
IntegerRepresentation DstSign = std::is_signed<Dst>::value |
? INTEGER_REPRESENTATION_SIGNED |
: INTEGER_REPRESENTATION_UNSIGNED, |
@@ -267,83 +275,111 @@ struct DstRangeRelationToSrcRangeImpl; |
// 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 constexpr 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::max()) <= DstLimits::max() || |
+ static_cast<Dst>(value) <= DstLimits::max(), |
+ static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || |
+ static_cast<Dst>(value) >= DstLimits::lowest()); |
+ } |
}; |
// 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 constexpr RangeConstraint Check(Src value) { |
- return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()), |
- (value >= NarrowingRange<Dst, Src>::lowest())); |
+ static constexpr RangeCheck Check(Src value) { |
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
+ return RangeCheck(value <= DstLimits::max(), value >= DstLimits::lowest()); |
} |
}; |
-// 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 constexpr RangeConstraint Check(Src value) { |
- return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true); |
+ static constexpr RangeCheck Check(Src value) { |
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
+ return RangeCheck( |
+ value <= DstLimits::max(), |
+ DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); |
} |
}; |
-// 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 constexpr RangeConstraint Check(Src value) { |
- return IntegerBitsPlusSign<Dst>::value > IntegerBitsPlusSign<Src>::value |
- ? RANGE_VALID |
- : GetRangeConstraint( |
- value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), |
- true); |
+ static constexpr RangeCheck Check(Src value) { |
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
+ using Promotion = decltype(Src() + Dst()); |
+ return RangeCheck(static_cast<Promotion>(value) <= |
+ static_cast<Promotion>(DstLimits::max()), |
+ DstLimits::lowest() <= Dst(0) || |
+ static_cast<Promotion>(value) >= |
+ static_cast<Promotion>(DstLimits::lowest())); |
} |
}; |
// 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 constexpr RangeConstraint Check(Src value) { |
- return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) |
- ? GetRangeConstraint(true, value >= static_cast<Src>(0)) |
- : GetRangeConstraint( |
- value <= static_cast<Src>(NarrowingRange<Dst, Src>::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( |
+ static_cast<Promotion>(SrcLimits::max()) <= |
+ static_cast<Promotion>(DstLimits::max()) || |
+ static_cast<Promotion>(value) <= |
+ static_cast<Promotion>(DstLimits::max()), |
+ value >= Src(0) && (DstLimits::lowest() == 0 || |
+ static_cast<Dst>(value) >= DstLimits::lowest())); |
} |
}; |
-template <typename Dst, typename Src> |
-constexpr RangeConstraint DstRangeRelationToSrcRange(Src 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."); |
- return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); |
+ 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. |
@@ -575,8 +611,8 @@ struct IsStrictOp { |
template <typename L, typename R> |
constexpr bool IsLessImpl(const L lhs, |
const R rhs, |
- const RangeConstraint l_range, |
- const RangeConstraint r_range) { |
+ const RangeCheck l_range, |
+ const RangeCheck r_range) { |
return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
(l_range == r_range && |
static_cast<decltype(lhs + rhs)>(lhs) < |
@@ -596,8 +632,8 @@ struct IsLess { |
template <typename L, typename R> |
constexpr bool IsLessOrEqualImpl(const L lhs, |
const R rhs, |
- const RangeConstraint l_range, |
- const RangeConstraint r_range) { |
+ const RangeCheck l_range, |
+ const RangeCheck r_range) { |
return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
(l_range == r_range && |
static_cast<decltype(lhs + rhs)>(lhs) <= |
@@ -617,8 +653,8 @@ struct IsLessOrEqual { |
template <typename L, typename R> |
constexpr bool IsGreaterImpl(const L lhs, |
const R rhs, |
- const RangeConstraint l_range, |
- const RangeConstraint r_range) { |
+ const RangeCheck l_range, |
+ const RangeCheck r_range) { |
return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
(l_range == r_range && |
static_cast<decltype(lhs + rhs)>(lhs) > |
@@ -638,8 +674,8 @@ struct IsGreater { |
template <typename L, typename R> |
constexpr bool IsGreaterOrEqualImpl(const L lhs, |
const R rhs, |
- const RangeConstraint l_range, |
- const RangeConstraint r_range) { |
+ const RangeCheck l_range, |
+ const RangeCheck r_range) { |
return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
(l_range == r_range && |
static_cast<decltype(lhs + rhs)>(lhs) >= |