Index: base/numerics/safe_math.h |
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h |
index 511eb23f78763c929e86db014f08911d17d0acf2..73a50b2a549fd9d439f4a9ce59afa11275c900af 100644 |
--- a/base/numerics/safe_math.h |
+++ b/base/numerics/safe_math.h |
@@ -46,6 +46,25 @@ namespace internal { |
// checked_size += HEADER LENGTH; |
// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) |
// Do stuff... |
+ |
+template <typename T> |
+class CheckedNumeric; |
+ |
+// Used to treat CheckedNumeric and arithmetic underlying types the same. |
+template <typename T> |
+struct UnderlyingType { |
+ using type = T; |
+ static const bool is_numeric = std::is_arithmetic<T>::value; |
+ static const bool is_checked = false; |
+}; |
+ |
+template <typename T> |
+struct UnderlyingType<CheckedNumeric<T>> { |
+ using type = T; |
+ static const bool is_numeric = true; |
+ static const bool is_checked = true; |
+}; |
+ |
template <typename T> |
class CheckedNumeric { |
static_assert(std::is_arithmetic<T>::value, |
@@ -59,10 +78,10 @@ class CheckedNumeric { |
// Copy constructor. |
template <typename Src> |
CheckedNumeric(const CheckedNumeric<Src>& rhs) |
- : state_(rhs.ValueUnsafe(), rhs.IsValid()) {} |
+ : state_(rhs.state_.value(), rhs.IsValid()) {} |
template <typename Src> |
- CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {} |
+ friend class CheckedNumeric; |
// This is not an explicit constructor because we implicitly upgrade regular |
// numerics to CheckedNumerics to make them easier to use. |
@@ -102,30 +121,30 @@ class CheckedNumeric { |
// crashing on a CHECK. |
T ValueFloating() const { |
static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float."); |
- return CheckedNumeric<T>::cast(*this).ValueUnsafe(); |
+ return state_.value(); |
} |
- // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now |
- // for tests and to avoid a big matrix of friend operator overloads. But the |
- // values it returns are unintuitive and likely to change in the future. |
- // Returns: the raw numeric value, regardless of the current state. |
- T ValueUnsafe() const { return state_.value(); } |
+ // This friend method is available solely for providing more detailed logging |
+ // in the the tests. Do not implement it in production code, because the |
+ // underlying values may change at any time. |
+ template <typename U> |
+ friend U GetNumericValueForTest(const CheckedNumeric<U>& src); |
// Prototypes for the supported arithmetic operator overloads. |
template <typename Src> |
- CheckedNumeric& operator+=(Src rhs); |
+ CheckedNumeric& operator+=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator-=(Src rhs); |
+ CheckedNumeric& operator-=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator*=(Src rhs); |
+ CheckedNumeric& operator*=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator/=(Src rhs); |
+ CheckedNumeric& operator/=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator%=(Src rhs); |
+ CheckedNumeric& operator%=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator<<=(Src rhs); |
+ CheckedNumeric& operator<<=(const Src rhs); |
template <typename Src> |
- CheckedNumeric& operator>>=(Src rhs); |
+ CheckedNumeric& operator>>=(const Src rhs); |
CheckedNumeric operator-() const { |
// Negation is always valid for floating point. |
@@ -173,84 +192,83 @@ class CheckedNumeric { |
return value; |
} |
- // These static methods behave like a convenience cast operator targeting |
- // the desired CheckedNumeric type. As an optimization, a reference is |
- // returned when Src is the same type as T. |
- template <typename Src> |
- static CheckedNumeric<T> cast( |
- Src u, |
- typename std::enable_if<std::numeric_limits<Src>::is_specialized, |
- int>::type = 0) { |
- return u; |
- } |
+ // These perform the actual math operations on the CheckedNumerics. |
+ // Binary arithmetic operations. |
+ template <template <typename, typename, typename> class M, |
+ typename L, |
+ typename R> |
+ static CheckedNumeric MathOp(const L& lhs, const R& rhs) { |
+ using Math = M<typename UnderlyingType<L>::type, |
+ typename UnderlyingType<R>::type, void>; |
+ T result = 0; |
+ bool is_valid = |
+ Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && |
+ Math::Op(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); |
+ return CheckedNumeric<T>(result, is_valid); |
+ }; |
+ |
+ // Assignment arithmetic operations. |
+ template <template <typename, typename, typename> class M, typename R> |
+ CheckedNumeric& MathOp(const R& rhs) { |
+ using Math = M<T, typename UnderlyingType<R>::type, void>; |
+ T result = 0; // Using T as the destination saves a range check. |
+ bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && |
+ Math::Op(state_.value(), Wrapper<R>::value(rhs), &result); |
+ *this = CheckedNumeric<T>(result, is_valid); |
+ return *this; |
+ }; |
+ |
+ private: |
+ CheckedNumericState<T> state_; |
template <typename Src> |
- static CheckedNumeric<T> cast( |
- const CheckedNumeric<Src>& u, |
- typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) { |
- return u; |
- } |
+ CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {} |
- static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; } |
+ // These wrappers allow us to handle state the same way for both |
+ // CheckedNumeric and POD arithmetic types. |
+ template <typename Src> |
+ struct Wrapper { |
+ static bool is_valid(Src) { return true; } |
+ static Src value(Src value) { return value; } |
+ }; |
- private: |
- CheckedNumericState<T> state_; |
+ template <typename Src> |
+ struct Wrapper<CheckedNumeric<Src>> { |
+ static bool is_valid(const CheckedNumeric<Src>& v) { return v.IsValid(); } |
+ static Src value(const CheckedNumeric<Src>& v) { return v.state_.value(); } |
+ }; |
}; |
-// This is the boilerplate for the standard arithmetic operator overloads. A |
-// macro isn't the prettiest solution, but it beats rewriting these five times. |
-// Some details worth noting are: |
-// * We apply the standard arithmetic promotions. |
-// * We skip range checks for floating points. |
-// * We skip range checks for destination integers with sufficient range. |
-// TODO(jschuh): extract these out into templates. |
-#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP, PROMOTION) \ |
- /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ |
- template <typename L, typename R> \ |
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \ |
- operator OP(const CheckedNumeric<L>& lhs, const CheckedNumeric<R>& rhs) { \ |
- using P = typename ArithmeticPromotion<PROMOTION, L, R>::type; \ |
- if (!rhs.IsValid() || !lhs.IsValid()) \ |
- return CheckedNumeric<P>(0, false); \ |
- /* Floating point always takes the fast path */ \ |
- if (std::is_floating_point<L>::value || std::is_floating_point<R>::value) \ |
- return CheckedNumeric<P>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ |
- P result = 0; \ |
- bool is_valid = \ |
- Checked##NAME(lhs.ValueUnsafe(), rhs.ValueUnsafe(), &result); \ |
- return CheckedNumeric<P>(result, is_valid); \ |
+// This is just boilerplate for the standard arithmetic operator overloads. |
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly. |
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ |
+ /* Binary arithmetic operator for all CheckedNumeric operations. */ \ |
+ template <typename L, typename R, \ |
+ typename std::enable_if<UnderlyingType<L>::is_numeric && \ |
+ UnderlyingType<R>::is_numeric && \ |
+ (UnderlyingType<L>::is_checked || \ |
+ UnderlyingType<R>::is_checked)>::type* = \ |
+ nullptr> \ |
+ CheckedNumeric< \ |
+ typename Checked##NAME<typename UnderlyingType<L>::type, \ |
+ typename UnderlyingType<R>::type>::result_type> \ |
+ operator OP(const L lhs, const R rhs) { \ |
+ return decltype(lhs OP rhs)::template MathOp<Checked##NAME>(lhs, rhs); \ |
} \ |
/* Assignment arithmetic operator implementation from CheckedNumeric. */ \ |
template <typename L> \ |
template <typename R> \ |
- CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(R rhs) { \ |
- *this = *this OP rhs; \ |
- return *this; \ |
- } \ |
- /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ |
- template <typename L, typename R, \ |
- typename std::enable_if<std::is_arithmetic<R>::value>::type* = \ |
- nullptr> \ |
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \ |
- operator OP(const CheckedNumeric<L>& lhs, R rhs) { \ |
- return lhs OP CheckedNumeric<R>(rhs); \ |
- } \ |
- /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \ |
- template <typename L, typename R, \ |
- typename std::enable_if<std::is_arithmetic<L>::value>::type* = \ |
- nullptr> \ |
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \ |
- operator OP(L lhs, const CheckedNumeric<R>& rhs) { \ |
- return CheckedNumeric<L>(lhs) OP rhs; \ |
+ CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \ |
+ return MathOp<Checked##NAME>(rhs); \ |
} |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=, MAX_EXPONENT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=, MAX_EXPONENT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=, MAX_EXPONENT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=, MAX_EXPONENT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=, MAX_EXPONENT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=, LEFT_PROMOTION) |
-BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=, LEFT_PROMOTION) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=) |
+BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=) |
#undef BASE_NUMERIC_ARITHMETIC_OPERATORS |