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

Unified Diff: base/numerics/safe_math_impl.h

Issue 2496143003: Simplify CheckedNumeric macros (Closed)
Patch Set: nits Created 4 years, 1 month 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_math.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_math_impl.h
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h
index 1886d6080c92189c4963a6f2d14cf56c34c8214b..7b9e53ab94c012263e4af764a5d1c453b03b9e99 100644
--- a/base/numerics/safe_math_impl.h
+++ b/base/numerics/safe_math_impl.h
@@ -127,13 +127,35 @@ constexpr T BinaryComplement(T x) {
return static_cast<T>(~x);
}
+// For integers less than 128-bit and floats 32-bit or larger, we have the type
+// with the larger maximum exponent take precedence.
+enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
+
+template <typename Lhs,
+ typename Rhs = Lhs,
+ ArithmeticPromotionCategory Promotion =
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION>
+struct ArithmeticPromotion;
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ typedef Lhs type;
+};
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ typedef Rhs type;
+};
+
// Here are the actual portable checked integer math implementations.
// TODO(jschuh): Break this code out from the enable_if pattern and find a clean
// way to coalesce things into the CheckedNumericState specializations below.
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedAdd(T x, T y, T* result) {
+CheckedAddImpl(T x, T y, T* result) {
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
@@ -150,9 +172,30 @@ CheckedAdd(T x, T y, T* result) {
y); // Unsigned is either valid or underflow.
}
+template <typename T, typename U, typename V>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<U>::is_integer &&
+ std::numeric_limits<V>::is_integer,
+ bool>::type
+CheckedAdd(T x, U y, V* result) {
+ using Promotion = typename ArithmeticPromotion<T, U>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) {
+ return false;
+ }
+
+ Promotion presult;
+ bool is_valid = CheckedAddImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+}
+
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedSub(T x, T y, T* result) {
+CheckedSubImpl(T x, T y, T* result) {
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
@@ -168,6 +211,27 @@ CheckedSub(T x, T y, T* result) {
: (x >= y);
}
+template <typename T, typename U, typename V>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<U>::is_integer &&
+ std::numeric_limits<V>::is_integer,
+ bool>::type
+CheckedSub(T x, U y, V* result) {
+ using Promotion = typename ArithmeticPromotion<T, U>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) {
+ return false;
+ }
+
+ Promotion presult;
+ bool is_valid = CheckedSubImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+}
+
// Integer multiplication is a bit complicated. In the fast case we just
// we just promote to a twice wider type, and range check the result. In the
// slow case we need to manually check that the result won't be truncated by
@@ -176,7 +240,7 @@ template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer &&
sizeof(T) * 2 <= sizeof(uintmax_t),
bool>::type
-CheckedMul(T x, T y, T* result) {
+CheckedMulImpl(T x, T y, T* result) {
typedef typename TwiceWiderInteger<T>::type IntermediateType;
IntermediateType tmp =
static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
@@ -189,7 +253,7 @@ typename std::enable_if<std::numeric_limits<T>::is_integer &&
std::numeric_limits<T>::is_signed &&
(sizeof(T) * 2 > sizeof(uintmax_t)),
bool>::type
-CheckedMul(T x, T y, T* result) {
+CheckedMulImpl(T x, T y, T* result) {
if (x && y) {
if (x > 0) {
if (y > 0) {
@@ -218,16 +282,37 @@ typename std::enable_if<std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed &&
(sizeof(T) * 2 > sizeof(uintmax_t)),
bool>::type
-CheckedMul(T x, T y, T* result) {
+CheckedMulImpl(T x, T y, T* result) {
*result = x * y;
return (y == 0 || x <= std::numeric_limits<T>::max() / y);
}
+template <typename T, typename U, typename V>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<U>::is_integer &&
+ std::numeric_limits<V>::is_integer,
+ bool>::type
+CheckedMul(T x, U y, V* result) {
+ using Promotion = typename ArithmeticPromotion<T, U>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) {
+ return false;
+ }
+
+ Promotion presult;
+ bool is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+}
+
// Division just requires a check for a zero denominator or an invalid negation
// on signed min/-1.
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
-CheckedDiv(T x, T y, T* result) {
+CheckedDivImpl(T x, T y, T* result) {
if (y && (!std::numeric_limits<T>::is_signed ||
x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
*result = x / y;
@@ -236,11 +321,32 @@ CheckedDiv(T x, T y, T* result) {
return false;
}
+template <typename T, typename U, typename V>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<U>::is_integer &&
+ std::numeric_limits<V>::is_integer,
+ bool>::type
+CheckedDiv(T x, U y, V* result) {
+ using Promotion = typename ArithmeticPromotion<T, U>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) {
+ return false;
+ }
+
+ Promotion presult;
+ bool is_valid = CheckedDivImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+}
+
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer &&
std::numeric_limits<T>::is_signed,
bool>::type
-CheckedMod(T x, T y, T* result) {
+CheckedModImpl(T x, T y, T* result) {
if (y > 0) {
*result = static_cast<T>(x % y);
return true;
@@ -252,7 +358,7 @@ template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
bool>::type
-CheckedMod(T x, T y, T* result) {
+CheckedModImpl(T x, T y, T* result) {
if (y != 0) {
*result = static_cast<T>(x % y);
return true;
@@ -260,6 +366,20 @@ CheckedMod(T x, T y, T* result) {
return false;
}
+template <typename T, typename U, typename V>
+typename std::enable_if<std::numeric_limits<T>::is_integer &&
+ std::numeric_limits<U>::is_integer &&
+ std::numeric_limits<V>::is_integer,
+ bool>::type
+CheckedMod(T x, U y, V* result) {
+ using Promotion = typename ArithmeticPromotion<T, U>::type;
+ Promotion presult;
+ bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+}
+
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer &&
std::numeric_limits<T>::is_signed,
@@ -344,12 +464,14 @@ typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
}
// These are the floating point stubs that the compiler needs to see.
-#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
- template <typename T> \
- typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type \
- Checked##NAME(T, T, T*) { \
- NOTREACHED(); \
- return static_cast<T>(false); \
+#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
+ template <typename T, typename U, typename V> \
+ typename std::enable_if<std::numeric_limits<T>::is_iec559 || \
+ std::numeric_limits<U>::is_iec559 || \
+ std::numeric_limits<V>::is_iec559, \
+ bool>::type Checked##NAME(T, U, V*) { \
+ NOTREACHED(); \
+ return static_cast<T>(false); \
}
BASE_FLOAT_ARITHMETIC_STUBS(Add)
@@ -474,43 +596,6 @@ class CheckedNumericState<T, NUMERIC_FLOATING> {
T value() const { return value_; }
};
-// For integers less than 128-bit and floats 32-bit or larger, we have the type
-// with the larger maximum exponent take precedence.
-enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
-
-template <typename Lhs,
- typename Rhs = Lhs,
- ArithmeticPromotionCategory Promotion =
- (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
- ? LEFT_PROMOTION
- : RIGHT_PROMOTION>
-struct ArithmeticPromotion;
-
-template <typename Lhs, typename Rhs>
-struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
- typedef Lhs type;
-};
-
-template <typename Lhs, typename Rhs>
-struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
- typedef Rhs type;
-};
-
-// 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>
-struct IsIntegerArithmeticSafe {
- static const bool value = !std::numeric_limits<T>::is_iec559 &&
- StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
- NUMERIC_RANGE_CONTAINED &&
- sizeof(T) >= (2 * sizeof(Lhs)) &&
- StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
- NUMERIC_RANGE_CONTAINED &&
- sizeof(T) >= (2 * sizeof(Rhs));
-};
-
} // namespace internal
} // namespace base
« no previous file with comments | « base/numerics/safe_math.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698