123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_NUMERICS_CHECKED_MATH_H_
- #define BASE_NUMERICS_CHECKED_MATH_H_
- #include <stddef.h>
- #include <limits>
- #include <type_traits>
- #include "base/numerics/checked_math_impl.h"
- namespace base {
- namespace internal {
- template <typename T>
- class CheckedNumeric {
- static_assert(std::is_arithmetic<T>::value,
- "CheckedNumeric<T>: T must be a numeric type.");
- public:
- using type = T;
- constexpr CheckedNumeric() = default;
- // Copy constructor.
- template <typename Src>
- constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
- : state_(rhs.state_.value(), rhs.IsValid()) {}
- template <typename Src>
- friend class CheckedNumeric;
- // This is not an explicit constructor because we implicitly upgrade regular
- // numerics to CheckedNumerics to make them easier to use.
- template <typename Src>
- constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
- : state_(value) {
- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
- }
- // This is not an explicit constructor because we want a seamless conversion
- // from StrictNumeric types.
- template <typename Src>
- constexpr CheckedNumeric(
- StrictNumeric<Src> value) // NOLINT(runtime/explicit)
- : state_(static_cast<Src>(value)) {}
- // IsValid() - The public API to test if a CheckedNumeric is currently valid.
- // A range checked destination type can be supplied using the Dst template
- // parameter.
- template <typename Dst = T>
- constexpr bool IsValid() const {
- return state_.is_valid() &&
- IsValueInRangeForNumericType<Dst>(state_.value());
- }
- // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
- // and is within the range supported by the destination type. Returns true if
- // successful and false otherwise.
- template <typename Dst>
- #if defined(__clang__) || defined(__GNUC__)
- __attribute__((warn_unused_result))
- #elif defined(_MSC_VER)
- _Check_return_
- #endif
- constexpr bool
- AssignIfValid(Dst* result) const {
- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
- ? ((*result = static_cast<Dst>(state_.value())), true)
- : false;
- }
- // ValueOrDie() - The primary accessor for the underlying value. If the
- // current state is not valid it will CHECK and crash.
- // A range checked destination type can be supplied using the Dst template
- // parameter, which will trigger a CHECK if the value is not in bounds for
- // the destination.
- // The CHECK behavior can be overridden by supplying a handler as a
- // template parameter, for test code, etc. However, the handler cannot access
- // the underlying value, and it is not available through other means.
- template <typename Dst = T, class CheckHandler = CheckOnFailure>
- constexpr StrictNumeric<Dst> ValueOrDie() const {
- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
- ? static_cast<Dst>(state_.value())
- : CheckHandler::template HandleFailure<Dst>();
- }
- // ValueOrDefault(T default_value) - A convenience method that returns the
- // current value if the state is valid, and the supplied default_value for
- // any other state.
- // A range checked destination type can be supplied using the Dst template
- // parameter. WARNING: This function may fail to compile or CHECK at runtime
- // if the supplied default_value is not within range of the destination type.
- template <typename Dst = T, typename Src>
- constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
- ? static_cast<Dst>(state_.value())
- : checked_cast<Dst>(default_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 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>
- constexpr CheckedNumeric& operator+=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator-=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator*=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator/=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator%=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator<<=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator>>=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator&=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator|=(const Src rhs);
- template <typename Src>
- constexpr CheckedNumeric& operator^=(const Src rhs);
- constexpr CheckedNumeric operator-() const {
- // The negation of two's complement int min is int min, so we simply
- // check for that in the constexpr case.
- // We use an optimized code path for a known run-time variable.
- return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
- std::is_floating_point<T>::value
- ? CheckedNumeric<T>(
- NegateWrapper(state_.value()),
- IsValid() && (!std::is_signed<T>::value ||
- std::is_floating_point<T>::value ||
- NegateWrapper(state_.value()) !=
- std::numeric_limits<T>::lowest()))
- : FastRuntimeNegate();
- }
- constexpr CheckedNumeric operator~() const {
- return CheckedNumeric<decltype(InvertWrapper(T()))>(
- InvertWrapper(state_.value()), IsValid());
- }
- constexpr CheckedNumeric Abs() const {
- return !IsValueNegative(state_.value()) ? *this : -*this;
- }
- template <typename U>
- constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
- const U rhs) const {
- using R = typename UnderlyingType<U>::type;
- using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
- // TODO(jschuh): This can be converted to the MathOp version and remain
- // constexpr once we have C++14 support.
- return CheckedNumeric<result_type>(
- static_cast<result_type>(
- IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
- ? state_.value()
- : Wrapper<U>::value(rhs)),
- state_.is_valid() && Wrapper<U>::is_valid(rhs));
- }
- template <typename U>
- constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
- const U rhs) const {
- using R = typename UnderlyingType<U>::type;
- using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
- // TODO(jschuh): This can be converted to the MathOp version and remain
- // constexpr once we have C++14 support.
- return CheckedNumeric<result_type>(
- static_cast<result_type>(
- IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
- ? state_.value()
- : Wrapper<U>::value(rhs)),
- state_.is_valid() && Wrapper<U>::is_valid(rhs));
- }
- // This function is available only for integral types. It returns an unsigned
- // integer of the same width as the source type, containing the absolute value
- // of the source, and properly handling signed min.
- constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
- UnsignedAbs() const {
- return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
- SafeUnsignedAbs(state_.value()), state_.is_valid());
- }
- constexpr CheckedNumeric& operator++() {
- *this += 1;
- return *this;
- }
- constexpr CheckedNumeric operator++(int) {
- CheckedNumeric value = *this;
- *this += 1;
- return value;
- }
- constexpr CheckedNumeric& operator--() {
- *this -= 1;
- return *this;
- }
- constexpr CheckedNumeric operator--(int) {
- CheckedNumeric value = *this;
- *this -= 1;
- return value;
- }
- // These perform the actual math operations on the CheckedNumerics.
- // Binary arithmetic operations.
- template <template <typename, typename, typename> class M,
- typename L,
- typename R>
- static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
- 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::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>
- constexpr CheckedNumeric& MathOp(const R rhs) {
- 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::Do(state_.value(), Wrapper<R>::value(rhs), &result);
- *this = CheckedNumeric<T>(result, is_valid);
- return *this;
- }
- private:
- CheckedNumericState<T> state_;
- CheckedNumeric FastRuntimeNegate() const {
- T result;
- bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
- return CheckedNumeric<T>(result, IsValid() && success);
- }
- template <typename Src>
- constexpr CheckedNumeric(Src value, bool is_valid)
- : state_(value, is_valid) {}
- // These wrappers allow us to handle state the same way for both
- // CheckedNumeric and POD arithmetic types.
- template <typename Src>
- struct Wrapper {
- static constexpr bool is_valid(Src) { return true; }
- static constexpr Src value(Src value) { return value; }
- };
- template <typename Src>
- struct Wrapper<CheckedNumeric<Src>> {
- static constexpr bool is_valid(const CheckedNumeric<Src> v) {
- return v.IsValid();
- }
- static constexpr Src value(const CheckedNumeric<Src> v) {
- return v.state_.value();
- }
- };
- template <typename Src>
- struct Wrapper<StrictNumeric<Src>> {
- static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
- static constexpr Src value(const StrictNumeric<Src> v) {
- return static_cast<Src>(v);
- }
- };
- };
- // Convenience functions to avoid the ugly template disambiguator syntax.
- template <typename Dst, typename Src>
- constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
- return value.template IsValid<Dst>();
- }
- template <typename Dst, typename Src>
- constexpr StrictNumeric<Dst> ValueOrDieForType(
- const CheckedNumeric<Src> value) {
- return value.template ValueOrDie<Dst>();
- }
- template <typename Dst, typename Src, typename Default>
- constexpr StrictNumeric<Dst> ValueOrDefaultForType(
- const CheckedNumeric<Src> value,
- const Default default_value) {
- return value.template ValueOrDefault<Dst>(default_value);
- }
- // Convience wrapper to return a new CheckedNumeric from the provided arithmetic
- // or CheckedNumericType.
- template <typename T>
- constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
- const T value) {
- return value;
- }
- // These implement the variadic wrapper for the math operations.
- template <template <typename, typename, typename> class M,
- typename L,
- typename R>
- constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
- 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);
- }
- // General purpose wrapper template for arithmetic operations.
- template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
- constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
- CheckMathOp(const L lhs, const R rhs, const Args... args) {
- return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
- }
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
- BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
- BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
- BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
- // These are some extra StrictNumeric operators to support simple pointer
- // arithmetic with our result types. Since wrapping on a pointer is always
- // bad, we trigger the CHECK condition here.
- template <typename L, typename R>
- L* operator+(L* lhs, const StrictNumeric<R> rhs) {
- uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
- CheckMul(sizeof(L), static_cast<R>(rhs)))
- .template ValueOrDie<uintptr_t>();
- return reinterpret_cast<L*>(result);
- }
- template <typename L, typename R>
- L* operator-(L* lhs, const StrictNumeric<R> rhs) {
- uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
- CheckMul(sizeof(L), static_cast<R>(rhs)))
- .template ValueOrDie<uintptr_t>();
- return reinterpret_cast<L*>(result);
- }
- } // namespace internal
- using internal::CheckedNumeric;
- using internal::IsValidForType;
- using internal::ValueOrDieForType;
- using internal::ValueOrDefaultForType;
- using internal::MakeCheckedNum;
- using internal::CheckMax;
- using internal::CheckMin;
- using internal::CheckAdd;
- using internal::CheckSub;
- using internal::CheckMul;
- using internal::CheckDiv;
- using internal::CheckMod;
- using internal::CheckLsh;
- using internal::CheckRsh;
- using internal::CheckAnd;
- using internal::CheckOr;
- using internal::CheckXor;
- } // namespace base
- #endif // BASE_NUMERICS_CHECKED_MATH_H_
|