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 |