| Index: base/numerics/safe_math.h
|
| diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
|
| index 470f4666981cc3788f85e7137807e31b4a1a79ec..598bd3e71847daf7fb46db9a07efe9c06452ede4 100644
|
| --- a/base/numerics/safe_math.h
|
| +++ b/base/numerics/safe_math.h
|
| @@ -13,7 +13,6 @@
|
| #include "base/numerics/safe_math_impl.h"
|
|
|
| namespace base {
|
| -
|
| namespace internal {
|
|
|
| // CheckedNumeric implements all the logic and operators for detecting integer
|
| @@ -22,15 +21,41 @@ namespace internal {
|
| // data types, and contains overloads for basic arithmetic operations (i.e.: +,
|
| // -, *, /, %).
|
| //
|
| +// You may also use one of the variadic convenience functions, which accept
|
| +// standard arithmetic or CheckedNumeric types, perform arithmetic operations,
|
| +// and return a CheckedNumeric result. The supported functions are:
|
| +// CheckAdd() - Addition.
|
| +// CheckSub() - Subtraction.
|
| +// CheckMul() - Multiplication.
|
| +// CheckDiv() - Division.
|
| +// CheckMod() - Modulous (integer only).
|
| +// CheckLsh() - Left integer shift (integer only).
|
| +// CheckRsh() - Right integer shift (integer only).
|
| +//
|
| +// The unary negation, increment, and decrement operators are supported, along
|
| +// with the following unary arithmetic methods, which return a new
|
| +// CheckedNumeric as a result of the operation:
|
| +// Abs() - Absolute value.
|
| +// UnsignedAbs() - Absolute value as an equival-width unsigned underlying type
|
| +// (valid for only integral types).
|
| +//
|
| // The following methods convert from CheckedNumeric to standard numeric values:
|
| -// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
|
| -// has not wrapped and is not the result of an invalid conversion).
|
| -// ValueOrDie() - Returns the underlying value. If the state is not valid this
|
| -// call will crash on a CHECK.
|
| -// ValueOrDefault() - Returns the current value, or the supplied default if the
|
| -// state is not valid.
|
| -// ValueFloating() - Returns the underlying floating point value (valid only
|
| -// only for floating point CheckedNumeric types).
|
| +// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
|
| +// has not wrapped and is not the result of an invalid conversion).
|
| +// ValueOrDie() - Returns the underlying value. If the state is not valid this
|
| +// call will crash on a CHECK.
|
| +// ValueOrDefault() - Returns the current value, or the supplied default if the
|
| +// state is not valid (will not trigger a CHECK).
|
| +// ValueFloating() - Returns the underlying floating point value (valid only
|
| +// for floating point CheckedNumeric types; will not cause a CHECK).
|
| +//
|
| +// The following are general utility methods that are useful for converting
|
| +// between arithmetic types and CheckedNumeric types:
|
| +// CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
|
| +// derived from casting the current instance to a CheckedNumeric of
|
| +// the supplied destination type.
|
| +// CheckNum() - Creates a new CheckedNumeric from the underlying type of the
|
| +// supplied arithmetic or CheckedNumeric type.
|
| //
|
| // Bitwise operations are explicitly not supported, because correct
|
| // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
|
| @@ -47,24 +72,6 @@ namespace internal {
|
| // 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,
|
| "CheckedNumeric<T>: T must be a numeric type.");
|
| @@ -122,6 +129,14 @@ class CheckedNumeric {
|
| return state_.value();
|
| }
|
|
|
| + // Returns a checked numeric of the specified type, cast from the current
|
| + // CheckedNumeric. If the current state is invalid or the destination cannot
|
| + // represent the result then the returned CheckedNumeric will be invalid.
|
| + template <typename Dst>
|
| + constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
|
| + return *this;
|
| + }
|
| +
|
| // 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.
|
| @@ -197,22 +212,21 @@ class CheckedNumeric {
|
| 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>;
|
| + using Math = typename MathWrapper<M, L, R>::math;
|
| 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);
|
| + Math::Do(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>;
|
| + using Math = typename MathWrapper<M, T, R>::math;
|
| 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);
|
| + Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
|
| *this = CheckedNumeric<T>(result, is_valid);
|
| return *this;
|
| };
|
| @@ -243,27 +257,83 @@ class CheckedNumeric {
|
| };
|
| };
|
|
|
| +// These variadic templates work out the return types.
|
| +// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
|
| +template <template <typename, typename, typename> class M,
|
| + typename L,
|
| + typename R,
|
| + typename... Args>
|
| +struct ResultType;
|
| +
|
| +template <template <typename, typename, typename> class M,
|
| + typename L,
|
| + typename R>
|
| +struct ResultType<M, L, R> {
|
| + using type = typename MathWrapper<M, L, R>::type;
|
| +};
|
| +
|
| +template <template <typename, typename, typename> class M,
|
| + typename L,
|
| + typename R,
|
| + typename... Args>
|
| +struct ResultType {
|
| + // The typedef was required here because MSVC fails to compile with "using".
|
| + typedef
|
| + typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type
|
| + type;
|
| +};
|
| +
|
| +// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
|
| +// or CheckedNumericType.
|
| +template <typename T>
|
| +CheckedNumeric<typename UnderlyingType<T>::type> CheckNum(T value) {
|
| + return value;
|
| +}
|
| +
|
| +// These implement the variadic wrapper for the math operations.
|
| +template <template <typename, typename, typename> class M,
|
| + typename L,
|
| + typename R>
|
| +CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
|
| + const R rhs) {
|
| + using Math = typename MathWrapper<M, L, R>::math;
|
| + return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
|
| + rhs);
|
| +};
|
| +
|
| +template <template <typename, typename, typename> class M,
|
| + typename L,
|
| + typename R,
|
| + typename... Args>
|
| +CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
|
| +ChkMathOp(const L lhs, const R rhs, const Args... args) {
|
| + auto tmp = ChkMathOp<M>(lhs, rhs);
|
| + return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
|
| + : decltype(ChkMathOp<M>(tmp, args...))(tmp);
|
| +};
|
| +
|
| // 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(const R rhs) { \
|
| - return MathOp<Checked##NAME>(rhs); \
|
| +#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<IsCheckedOp<L, R>::value>::type* = \
|
| + nullptr> \
|
| + CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type> \
|
| + operator OP(const L lhs, const R rhs) { \
|
| + return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \
|
| + } \
|
| + /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
|
| + template <typename L> \
|
| + template <typename R> \
|
| + CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
|
| + return MathOp<Checked##NAME##Op>(rhs); \
|
| + } \
|
| + /* Variadic arithmetic functions that return CheckedNumeric. */ \
|
| + template <typename L, typename R, typename... Args> \
|
| + CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \
|
| + Check##NAME(const L lhs, const R rhs, const Args... args) { \
|
| + return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...); \
|
| }
|
|
|
| BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
|
| @@ -271,14 +341,22 @@ 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, >>, >>=)
|
| +BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=)
|
| +BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=)
|
|
|
| #undef BASE_NUMERIC_ARITHMETIC_OPERATORS
|
|
|
| } // namespace internal
|
|
|
| using internal::CheckedNumeric;
|
| +using internal::CheckNum;
|
| +using internal::CheckAdd;
|
| +using internal::CheckSub;
|
| +using internal::CheckMul;
|
| +using internal::CheckDiv;
|
| +using internal::CheckMod;
|
| +using internal::CheckLsh;
|
| +using internal::CheckRsh;
|
|
|
| } // namespace base
|
|
|
|
|