Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(838)

Unified Diff: base/numerics/safe_conversions_impl.h

Issue 2585043002: Improve saturated_cast performance (Closed)
Patch Set: fix comment nit Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/numerics/safe_conversions.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..fd0064dcad3eeded515b1578220acbed111975ac 100644
--- a/base/numerics/safe_conversions_impl.h
+++ b/base/numerics/safe_conversions_impl.h
@@ -166,29 +166,38 @@ 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.
+enum RangeConstraintEnum {
+ RANGE_VALID = 0x0, // Value can be represented by the destination type.
+ RANGE_OVERFLOW = 0x1, // Value would overflow.
+ RANGE_UNDERFLOW = 0x2, // Value would underflow.
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 RangeConstraint {
+ public:
+ constexpr RangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound)
+ : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {}
+ constexpr RangeConstraint() : 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 RangeConstraintEnum() const {
+ return static_cast<RangeConstraintEnum>(
+ static_cast<int>(is_overflow_) | static_cast<int>(is_underflow_) << 1);
+ }
+ constexpr bool operator==(const RangeConstraintEnum rhs) const {
+ return rhs == static_cast<RangeConstraintEnum>(*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,112 @@ 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 RangeConstraint Check(Src value) {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeConstraint(
+ 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()));
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeConstraint(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);
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeConstraint(
+ 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);
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeConstraint(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));
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeConstraint(
+ 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>
+template <typename Dst,
+ template <typename> class Bounds = std::numeric_limits,
+ typename Src>
constexpr RangeConstraint 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.
« no previous file with comments | « base/numerics/safe_conversions.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698