/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* mfbt maths algorithms. */ #ifndef mozilla_MathAlgorithms_h #define mozilla_MathAlgorithms_h #include #include #include #include #include #include #include "mozilla/Assertions.h" namespace mozilla { namespace detail { template struct AbsReturnType; template struct AbsReturnType< T, std::enable_if_t && std::is_signed_v>> { using Type = std::make_unsigned_t; }; template struct AbsReturnType>> { using Type = T; }; } // namespace detail template inline constexpr typename detail::AbsReturnType::Type Abs(const T aValue) { using ReturnType = typename detail::AbsReturnType::Type; return aValue >= 0 ? ReturnType(aValue) : ~ReturnType(aValue) + 1; } template <> inline float Abs(const float aFloat) { return std::fabs(aFloat); } template <> inline double Abs(const double aDouble) { return std::fabs(aDouble); } template <> inline long double Abs(const long double aLongDouble) { return std::fabs(aLongDouble); } /** * Compute the log of the least power of 2 greater than or equal to |aValue|. * * CeilingLog2(0..1) is 0; * CeilingLog2(2) is 1; * CeilingLog2(3..4) is 2; * CeilingLog2(5..8) is 3; * CeilingLog2(9..16) is 4; and so on. */ template constexpr uint_fast8_t CeilingLog2(const T aValue) { static_assert(std::is_integral_v && std::is_unsigned_v); return aValue <= 1 ? 0u : static_cast( std::bit_width(static_cast(aValue - 1))); } /** A CeilingLog2 variant that accepts only size_t. */ constexpr uint_fast8_t CeilingLog2Size(size_t aValue) { return CeilingLog2(aValue); } /** * Compute the bit position of the most significant bit set in * |aValue|. Requires that |aValue| is non-zero. */ template constexpr uint_fast8_t FindMostSignificantBit(T aValue) { static_assert(std::is_integral_v && std::is_unsigned_v); MOZ_ASSERT(aValue != 0); return static_cast(std::bit_width(aValue) - 1); } /** * Compute the log of the greatest power of 2 less than or equal to |aValue|. * * FloorLog2(0..1) is 0; * FloorLog2(2..3) is 1; * FloorLog2(4..7) is 2; * FloorLog2(8..15) is 3; and so on. */ template constexpr uint_fast8_t FloorLog2(const T aValue) { return FindMostSignificantBit(static_cast(aValue | 1)); } /** A FloorLog2 variant that accepts only size_t. */ constexpr uint_fast8_t FloorLog2Size(size_t aValue) { return FloorLog2(aValue); } /* * Compute the smallest power of 2 greater than or equal to |x|. |x| must not * be so great that the computed value would overflow |size_t|. */ constexpr size_t RoundUpPow2(size_t aValue) { MOZ_ASSERT(aValue <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)), "can't round up -- will overflow!"); return std::bit_ceil(aValue); } /** * Rotates the bits of the given value left by the amount of the shift width. */ template MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW constexpr T RotateLeft(const T aValue, uint_fast8_t aShift) { MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!"); return std::rotl(aValue, aShift); } /** * Rotates the bits of the given value right by the amount of the shift width. */ template MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW constexpr T RotateRight(const T aValue, uint_fast8_t aShift) { MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!"); return std::rotr(aValue, aShift); } // Greatest Common Divisor, from // https://en.wikipedia.org/wiki/Binary_GCD_algorithm#Implementation template MOZ_ALWAYS_INLINE T GCD(T aA, T aB) { static_assert(std::is_integral_v); MOZ_ASSERT(aA >= 0); MOZ_ASSERT(aB >= 0); if (aA == 0) { return aB; } if (aB == 0) { return aA; } using UnsignedT = std::make_unsigned_t; auto az = std::countr_zero(static_cast(aA)); auto bz = std::countr_zero(static_cast(aB)); auto shift = std::min(az, bz); aA >>= az; aB >>= bz; while (aA != 0) { if constexpr (!std::is_signed_v) { if (aA < aB) { std::swap(aA, aB); } } T diff = aA - aB; if constexpr (std::is_signed_v) { aB = std::min(aA, aB); } if constexpr (std::is_signed_v) { aA = std::abs(diff); } else { aA = diff; } if (aA) { aA >>= std::countr_zero(static_cast(aA)); } } return aB << shift; } } /* namespace mozilla */ #endif /* mozilla_MathAlgorithms_h */