Index: base/numerics/safe_math_impl.h |
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h |
index f6f9f5d568344b7ab7270efc5f666b4b84b9f52a..8d4b0e5a8818deb880816c0943fc0c06771d40ab 100644 |
--- a/base/numerics/safe_math_impl.h |
+++ b/base/numerics/safe_math_impl.h |
@@ -129,21 +129,6 @@ constexpr T BinaryComplement(T x) { |
return static_cast<T>(~x); |
} |
-// Return if a numeric value is negative regardless of type. |
-template <typename T, |
- typename std::enable_if<std::is_arithmetic<T>::value && |
- std::is_signed<T>::value>::type* = nullptr> |
-constexpr bool IsNegative(T x) { |
- return x < 0; |
-} |
- |
-template <typename T, |
- typename std::enable_if<std::is_arithmetic<T>::value && |
- !std::is_signed<T>::value>::type* = nullptr> |
-constexpr bool IsNegative(T x) { |
- return false; |
-} |
- |
enum ArithmeticPromotionCategory { |
LEFT_PROMOTION, // Use the type of the left-hand argument. |
RIGHT_PROMOTION, // Use the type of the right-hand argument. |
@@ -213,21 +198,7 @@ struct BigEnoughPromotion<Lhs, Rhs, true, false> { |
static const bool is_contained = false; |
}; |
-// These are the four supported promotion types. |
- |
-// Use the type of the left-hand argument. |
-template <typename Lhs, typename Rhs> |
-struct ArithmeticPromotion<LEFT_PROMOTION, Lhs, Rhs> { |
- using type = Lhs; |
- static const bool is_contained = true; |
-}; |
- |
-// Use the type of the right-hand argument. |
-template <typename Lhs, typename Rhs> |
-struct ArithmeticPromotion<RIGHT_PROMOTION, Lhs, Rhs> { |
- using type = Rhs; |
- static const bool is_contained = true; |
-}; |
+// These are the supported promotion types. |
// Use the type supporting the largest exponent. |
template <typename Lhs, typename Rhs> |
@@ -290,33 +261,41 @@ CheckedAddImpl(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) { |
+template <typename T, typename U, class Enable = void> |
+struct CheckedAdd {}; |
+ |
+template <typename T, typename U> |
+struct CheckedAdd< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
+ template <typename V> |
+ static bool Op(T x, U y, V* result) { |
#if USE_OVERFLOW_BUILTINS |
- return !__builtin_add_overflow(x, y, result); |
+ return !__builtin_add_overflow(x, y, result); |
#else |
- using Promotion = |
- typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
- Promotion presult; |
- // 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. |
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
- IsValueInRangeForNumericType<Promotion>(y); |
- |
- if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
- presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); |
- } else { |
- is_valid &= CheckedAddImpl(static_cast<Promotion>(x), |
- static_cast<Promotion>(y), &presult); |
- } |
- *result = static_cast<V>(presult); |
- return is_valid && IsValueInRangeForNumericType<V>(presult); |
+ using Promotion = |
+ typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
+ Promotion presult; |
+ // 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. |
+ bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
+ IsValueInRangeForNumericType<Promotion>(y); |
+ |
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
+ presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); |
+ } else { |
+ is_valid &= CheckedAddImpl(static_cast<Promotion>(x), |
+ static_cast<Promotion>(y), &presult); |
+ } |
+ *result = static_cast<V>(presult); |
+ return is_valid && IsValueInRangeForNumericType<V>(presult); |
#endif |
-} |
+ } |
+}; |
template <typename T> |
typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
@@ -336,33 +315,41 @@ CheckedSubImpl(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) { |
+template <typename T, typename U, class Enable = void> |
+struct CheckedSub {}; |
+ |
+template <typename T, typename U> |
+struct CheckedSub< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
+ template <typename V> |
+ static bool Op(T x, U y, V* result) { |
#if USE_OVERFLOW_BUILTINS |
- return !__builtin_sub_overflow(x, y, result); |
+ return !__builtin_sub_overflow(x, y, result); |
#else |
- using Promotion = |
- typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
- Promotion presult; |
- // 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. |
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
- IsValueInRangeForNumericType<Promotion>(y); |
- |
- if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
- presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); |
- } else { |
- is_valid &= CheckedSubImpl(static_cast<Promotion>(x), |
- static_cast<Promotion>(y), &presult); |
- } |
- *result = static_cast<V>(presult); |
- return is_valid && IsValueInRangeForNumericType<V>(presult); |
+ using Promotion = |
+ typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
+ Promotion presult; |
+ // 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. |
+ bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
+ IsValueInRangeForNumericType<Promotion>(y); |
+ |
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
+ presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); |
+ } else { |
+ is_valid &= CheckedSubImpl(static_cast<Promotion>(x), |
+ static_cast<Promotion>(y), &presult); |
+ } |
+ *result = static_cast<V>(presult); |
+ return is_valid && IsValueInRangeForNumericType<V>(presult); |
#endif |
-} |
+ } |
+}; |
// 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 |
@@ -419,43 +406,52 @@ CheckedMulImpl(T x, T y, T* result) { |
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) { |
+template <typename T, typename U, class Enable = void> |
+struct CheckedMul {}; |
+ |
+template <typename T, typename U> |
+struct CheckedMul< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
+ template <typename V> |
+ static bool Op(T x, U y, V* result) { |
#if USE_OVERFLOW_BUILTINS |
#if defined(__clang__) |
- // TODO(jschuh): Get the Clang runtime library issues sorted out so we can |
- // support full-width, mixed-sign multiply builtins. https://crbug.com/613003 |
- static const bool kUseMaxInt = |
- sizeof(__typeof__(x * y)) < sizeof(intptr_t) || |
- (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && |
- std::is_signed<T>::value == std::is_signed<U>::value); |
+ // TODO(jschuh): Get the Clang runtime library issues sorted out so we can |
+ // support full-width, mixed-sign multiply builtins. |
+ // https://crbug.com/613003 |
+ static const bool kUseMaxInt = |
+ sizeof(__typeof__(x * y)) < sizeof(intptr_t) || |
+ (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && |
+ std::is_signed<T>::value == std::is_signed<U>::value); |
#else |
- static const bool kUseMaxInt = true; |
+ static const bool kUseMaxInt = true; |
#endif |
- if (kUseMaxInt) |
- return !__builtin_mul_overflow(x, y, result); |
+ if (kUseMaxInt) |
+ return !__builtin_mul_overflow(x, y, result); |
#endif |
- using Promotion = |
- typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
- Promotion presult; |
- // 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. |
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
- IsValueInRangeForNumericType<Promotion>(y); |
- |
- if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
- presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
- } else { |
- is_valid &= CheckedMulImpl(static_cast<Promotion>(x), |
- static_cast<Promotion>(y), &presult); |
+ using Promotion = |
+ typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
+ Promotion presult; |
+ // 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. |
+ bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
+ IsValueInRangeForNumericType<Promotion>(y); |
+ |
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
+ presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
+ } else { |
+ is_valid &= CheckedMulImpl(static_cast<Promotion>(x), |
+ static_cast<Promotion>(y), &presult); |
+ } |
+ *result = static_cast<V>(presult); |
+ return is_valid && IsValueInRangeForNumericType<V>(presult); |
} |
- *result = static_cast<V>(presult); |
- return is_valid && IsValueInRangeForNumericType<V>(presult); |
-} |
+}; |
// Avoid poluting the namespace once we're done with the macro. |
#undef USE_OVERFLOW_BUILTINS |
@@ -473,24 +469,32 @@ CheckedDivImpl(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<BIG_ENOUGH_PROMOTION, T, U>::type; |
- Promotion presult; |
- // 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. |
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
- IsValueInRangeForNumericType<Promotion>(y); |
- 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 U, class Enable = void> |
+struct CheckedDiv {}; |
+ |
+template <typename T, typename U> |
+struct CheckedDiv< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
+ template <typename V> |
+ static bool Op(T x, U y, V* result) { |
+ using Promotion = |
+ typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
+ Promotion presult; |
+ // 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. |
+ bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
+ IsValueInRangeForNumericType<Promotion>(y); |
+ 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 && |
@@ -516,62 +520,84 @@ CheckedModImpl(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<BIG_ENOUGH_PROMOTION, 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 U, class Enable = void> |
+struct CheckedMod {}; |
+ |
+template <typename T, typename U> |
+struct CheckedMod< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
+ template <typename V> |
+ static bool Op(T x, U y, V* result) { |
+ using Promotion = |
+ typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, 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 U, class Enable = void> |
+struct CheckedLeftShift {}; |
// Left shift. Shifts less than 0 or greater than or equal to the number |
// of bits in the promoted type are undefined. Shifts of negative values |
// are undefined. Otherwise it is defined when the result fits. |
-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 |
-CheckedLeftShift(T x, U shift, V* result) { |
- using ShiftType = typename UnsignedIntegerForSize<T>::type; |
- static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); |
- const ShiftType real_shift = static_cast<ShiftType>(shift); |
- // Signed shift is not legal on negative values. |
- if (!IsNegative(x) && real_shift < kBitWidth) { |
- // Just use a multiplication because it's easy. |
- // TODO(jschuh): This could probably be made more efficient. |
- if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) |
- return CheckedMul(x, static_cast<T>(1) << shift, result); |
- return !x; // Special case zero for a full width signed shift. |
+template <typename T, typename U> |
+struct CheckedLeftShift< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = T; |
+ template <typename V> |
+ static bool Op(T x, U shift, V* result) { |
+ using ShiftType = typename UnsignedIntegerForSize<T>::type; |
+ static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); |
+ const ShiftType real_shift = static_cast<ShiftType>(shift); |
+ // Signed shift is not legal on negative values. |
+ if (!IsValueNegative(x) && real_shift < kBitWidth) { |
Tom Sepez
2016/11/21 17:19:52
nit: while we're at it, these might read easier if
jschuh
2016/11/21 17:43:25
I'm going to leave it for now, because when I look
|
+ // Just use a multiplication because it's easy. |
+ // TODO(jschuh): This could probably be made more efficient. |
+ if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) |
+ return CheckedMul<T, T>::Op(x, static_cast<T>(1) << shift, result); |
+ return !x; // Special case zero for a full width signed shift. |
+ } |
+ return false; |
} |
- return false; |
-} |
+}; |
+ |
+template <typename T, typename U, class Enable = void> |
+struct CheckedRightShift {}; |
// Right shift. Shifts less than 0 or greater than or equal to the number |
// of bits in the promoted type are undefined. Otherwise, it is always defined, |
// but a right shift of a negative value is implementation-dependent. |
-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 |
-CheckedRightShift(T x, U shift, V* result) { |
- // Use the type conversion push negative values out of range. |
- using ShiftType = typename UnsignedIntegerForSize<T>::type; |
- if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { |
- T tmp = x >> shift; |
- *result = static_cast<V>(tmp); |
- return IsValueInRangeForNumericType<unsigned>(tmp); |
+template <typename T, typename U> |
+struct CheckedRightShift< |
+ T, |
+ U, |
+ typename std::enable_if<std::numeric_limits<T>::is_integer && |
+ std::numeric_limits<U>::is_integer>::type> { |
+ using result_type = T; |
+ template <typename V = result_type> |
+ static bool Op(T x, U shift, V* result) { |
+ // Use the type conversion push negative values out of range. |
+ using ShiftType = typename UnsignedIntegerForSize<T>::type; |
+ if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { |
+ T tmp = x >> shift; |
+ *result = static_cast<V>(tmp); |
+ return IsValueInRangeForNumericType<V>(tmp); |
+ } |
+ return false; |
} |
- return false; |
-} |
+}; |
template <typename T> |
typename std::enable_if<std::numeric_limits<T>::is_integer && |
@@ -640,40 +666,31 @@ SafeUnsignedAbs(T value) { |
return static_cast<T>(value); |
} |
-template <typename T> |
-typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( |
- T value, |
- bool*) { |
- NOTREACHED(); |
- return static_cast<T>(-value); |
-} |
- |
-template <typename T> |
-typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( |
- T value, |
- bool*) { |
- NOTREACHED(); |
- return static_cast<T>(std::abs(value)); |
-} |
- |
-// These are the floating point stubs that the compiler needs to see. |
-#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) |
-BASE_FLOAT_ARITHMETIC_STUBS(Sub) |
-BASE_FLOAT_ARITHMETIC_STUBS(Mul) |
-BASE_FLOAT_ARITHMETIC_STUBS(Div) |
-BASE_FLOAT_ARITHMETIC_STUBS(Mod) |
- |
-#undef BASE_FLOAT_ARITHMETIC_STUBS |
+// This is just boilerplate that wraps the standard floating point arithmetic. |
Tom Sepez
2016/11/21 17:19:52
Maybe this belongs in the safe_math.h file along w
jschuh
2016/11/21 17:43:25
Nah, this is the file with all of the integer impl
|
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly. |
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ |
+ template <typename T, typename U> \ |
+ struct Checked##NAME<T, U, typename std::enable_if< \ |
+ std::numeric_limits<T>::is_iec559 || \ |
+ std::numeric_limits<U>::is_iec559>::type> { \ |
+ using result_type = \ |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ |
+ template <typename V> \ |
+ static bool Op(T x, U y, V* result) { \ |
+ using Promotion = \ |
+ typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ |
+ Promotion presult = x OP y; \ |
+ *result = static_cast<V>(presult); \ |
+ return IsValueInRangeForNumericType<V>(presult); \ |
+ } \ |
+ }; |
+ |
+BASE_FLOAT_ARITHMETIC_OPS(Add, +) |
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -) |
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *) |
+BASE_FLOAT_ARITHMETIC_OPS(Div, /) |
+ |
+#undef BASE_FLOAT_ARITHMETIC_OPS |
template <typename T> |
typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type |