// Copyright 2017 The Chromium Authors // 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 #include #include #include "base/numerics/checked_math_impl.h" namespace base { namespace internal { template class CheckedNumeric { static_assert(std::is_arithmetic::value, "CheckedNumeric: T must be a numeric type."); public: template friend class CheckedNumeric; using type = T; constexpr CheckedNumeric() = default; // Copy constructor. template constexpr CheckedNumeric(const CheckedNumeric& rhs) : state_(rhs.state_.value(), rhs.IsValid()) {} // Strictly speaking, this is not necessary, but declaring this allows class // template argument deduction to be used so that it is possible to simply // write `CheckedNumeric(777)` instead of `CheckedNumeric(777)`. // NOLINTNEXTLINE(google-explicit-constructor) constexpr CheckedNumeric(T value) : state_(value) {} // This is not an explicit constructor because we implicitly upgrade regular // numerics to CheckedNumerics to make them easier to use. template ::value>> // NOLINTNEXTLINE(google-explicit-constructor) constexpr CheckedNumeric(Src value) : state_(value) {} // This is not an explicit constructor because we want a seamless conversion // from StrictNumeric types. template // NOLINTNEXTLINE(google-explicit-constructor) constexpr CheckedNumeric(StrictNumeric value) : state_(static_cast(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 constexpr bool IsValid() const { return state_.is_valid() && IsValueInRangeForNumericType(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 #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()) ? ((*result = static_cast(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 constexpr StrictNumeric ValueOrDie() const { return BASE_NUMERICS_LIKELY(IsValid()) ? static_cast(state_.value()) : CheckHandler::template HandleFailure(); } // 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 constexpr StrictNumeric ValueOrDefault(const Src default_value) const { return BASE_NUMERICS_LIKELY(IsValid()) ? static_cast(state_.value()) : checked_cast(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 constexpr CheckedNumeric::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 friend U GetNumericValueForTest(const CheckedNumeric& src); // Prototypes for the supported arithmetic operator overloads. template constexpr CheckedNumeric& operator+=(const Src rhs); template constexpr CheckedNumeric& operator-=(const Src rhs); template constexpr CheckedNumeric& operator*=(const Src rhs); template constexpr CheckedNumeric& operator/=(const Src rhs); template constexpr CheckedNumeric& operator%=(const Src rhs); template constexpr CheckedNumeric& operator<<=(const Src rhs); template constexpr CheckedNumeric& operator>>=(const Src rhs); template constexpr CheckedNumeric& operator&=(const Src rhs); template constexpr CheckedNumeric& operator|=(const Src rhs); template constexpr CheckedNumeric& operator^=(const Src rhs); constexpr CheckedNumeric operator-() const { // Use an optimized code path for a known run-time variable. if (!IsConstantEvaluated() && std::is_signed::value && std::is_floating_point::value) { return FastRuntimeNegate(); } // The negation of two's complement int min is int min. const bool is_valid = IsValid() && (!std::is_signed::value || std::is_floating_point::value || NegateWrapper(state_.value()) != std::numeric_limits::lowest()); return CheckedNumeric(NegateWrapper(state_.value()), is_valid); } constexpr CheckedNumeric operator~() const { return CheckedNumeric( InvertWrapper(state_.value()), IsValid()); } constexpr CheckedNumeric Abs() const { return !IsValueNegative(state_.value()) ? *this : -*this; } template constexpr CheckedNumeric::type> Max( const U rhs) const { return CheckMax(*this, rhs); } template constexpr CheckedNumeric::type> Min( const U rhs) const { return CheckMin(*this, 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::type> UnsignedAbs() const { return CheckedNumeric::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) { // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20. const CheckedNumeric value = *this; *this -= 1; return value; } // These perform the actual math operations on the CheckedNumerics. // Binary arithmetic operations. template