// 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 #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: using type = T; constexpr CheckedNumeric() = default; // Copy constructor. template constexpr CheckedNumeric(const CheckedNumeric& rhs) : state_(rhs.state_.value(), rhs.IsValid()) {} template friend class CheckedNumeric; // This is not an explicit constructor because we implicitly upgrade regular // numerics to CheckedNumerics to make them easier to use. template constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) : state_(value) { static_assert(std::is_arithmetic::value, "Argument must be numeric."); } // This is not an explicit constructor because we want a seamless conversion // from StrictNumeric types. template constexpr CheckedNumeric( StrictNumeric value) // NOLINT(runtime/explicit) : 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 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 { // 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::value || std::is_floating_point::value ? CheckedNumeric( NegateWrapper(state_.value()), IsValid() && (!std::is_signed::value || std::is_floating_point::value || NegateWrapper(state_.value()) != std::numeric_limits::lowest())) : FastRuntimeNegate(); } 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 { using R = typename UnderlyingType::type; using result_type = typename MathWrapper::type; // TODO(jschuh): This can be converted to the MathOp version and remain // constexpr once we have C++14 support. return CheckedNumeric( static_cast( IsGreater::Test(state_.value(), Wrapper::value(rhs)) ? state_.value() : Wrapper::value(rhs)), state_.is_valid() && Wrapper::is_valid(rhs)); } template constexpr CheckedNumeric::type> Min( const U rhs) const { using R = typename UnderlyingType::type; using result_type = typename MathWrapper::type; // TODO(jschuh): This can be converted to the MathOp version and remain // constexpr once we have C++14 support. return CheckedNumeric( static_cast( IsLess::Test(state_.value(), Wrapper::value(rhs)) ? state_.value() : Wrapper::value(rhs)), state_.is_valid() && Wrapper::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::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) { CheckedNumeric value = *this; *this -= 1; return value; } // These perform the actual math operations on the CheckedNumerics. // Binary arithmetic operations. template