/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/Assertions.h" #include "mozilla/TypeTraits.h" #include #include #include namespace mozilla { // Greatest Common Divisor template MOZ_ALWAYS_INLINE IntegerType EuclidGCD(IntegerType a, IntegerType b) { // Euclid's algorithm; O(N) in the worst case. (There are better // ways, but we don't need them for the current use of this algo.) MOZ_ASSERT(a > 0); MOZ_ASSERT(b > 0); while (a != b) { if (a > b) { a = a - b; } else { b = b - a; } } return a; } // Least Common Multiple template MOZ_ALWAYS_INLINE IntegerType EuclidLCM(IntegerType a, IntegerType b) { // Divide first to reduce overflow risk. return (a / EuclidGCD(a, b)) * b; } namespace detail { template struct AllowDeprecatedAbsFixed : FalseType {}; template<> struct AllowDeprecatedAbsFixed : TrueType {}; template<> struct AllowDeprecatedAbsFixed : TrueType {}; template struct AllowDeprecatedAbs : AllowDeprecatedAbsFixed {}; template<> struct AllowDeprecatedAbs : TrueType {}; template<> struct AllowDeprecatedAbs : TrueType {}; } // namespace detail // DO NOT USE DeprecatedAbs. It exists only until its callers can be converted // to Abs below, and it will be removed when all callers have been changed. template inline typename mozilla::EnableIf::value, T>::Type DeprecatedAbs(const T t) { // The absolute value of the smallest possible value of a signed-integer type // won't fit in that type (on twos-complement systems -- and we're blithely // assuming we're on such systems, for the non- types listed above), // so assert that the input isn't that value. // // This is the case if: the value is non-negative; or if adding one (giving a // value in the range [-maxvalue, 0]), then negating (giving a value in the // range [0, maxvalue]), doesn't produce maxvalue (because in twos-complement, // (minvalue + 1) == -maxvalue). MOZ_ASSERT(t >= 0 || -(t + 1) != T((1ULL << (CHAR_BIT * sizeof(T) - 1)) - 1), "You can't negate the smallest possible negative integer!"); return t >= 0 ? t : -t; } namespace detail { // For now mozilla::Abs only takes intN_T, the signed natural types, and // float/double/long double. Feel free to add overloads for other standard, // signed types if you need them. template struct AbsReturnTypeFixed; template<> struct AbsReturnTypeFixed { typedef uint8_t Type; }; template<> struct AbsReturnTypeFixed { typedef uint16_t Type; }; template<> struct AbsReturnTypeFixed { typedef uint32_t Type; }; template<> struct AbsReturnTypeFixed { typedef uint64_t Type; }; template struct AbsReturnType : AbsReturnTypeFixed {}; template<> struct AbsReturnType : EnableIf {}; template<> struct AbsReturnType { typedef unsigned char Type; }; template<> struct AbsReturnType { typedef unsigned short Type; }; template<> struct AbsReturnType { typedef unsigned int Type; }; template<> struct AbsReturnType { typedef unsigned long Type; }; template<> struct AbsReturnType { typedef unsigned long long Type; }; template<> struct AbsReturnType { typedef float Type; }; template<> struct AbsReturnType { typedef double Type; }; template<> struct AbsReturnType { typedef long double Type; }; } // namespace detail template inline typename detail::AbsReturnType::Type Abs(const T t) { typedef typename detail::AbsReturnType::Type ReturnType; return t >= 0 ? ReturnType(t) : ~ReturnType(t) + 1; } template<> inline float Abs(const float f) { return std::fabs(f); } template<> inline double Abs(const double d) { return std::fabs(d); } template<> inline long double Abs(const long double d) { return std::fabs(d); } } // namespace mozilla #if defined(_WIN32) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) # define MOZ_BITSCAN_WINDOWS extern "C" { unsigned char _BitScanForward(unsigned long* Index, unsigned long mask); unsigned char _BitScanReverse(unsigned long* Index, unsigned long mask); # pragma intrinsic(_BitScanForward, _BitScanReverse) # if defined(_M_AMD64) || defined(_M_X64) # define MOZ_BITSCAN_WINDOWS64 unsigned char _BitScanForward64(unsigned long* index, unsigned __int64 mask); unsigned char _BitScanReverse64(unsigned long* index, unsigned __int64 mask); # pragma intrinsic(_BitScanForward64, _BitScanReverse64) # endif } // extern "C" #endif namespace mozilla { namespace detail { #if defined(MOZ_BITSCAN_WINDOWS) inline uint_fast8_t CountLeadingZeroes32(uint32_t u) { unsigned long index; _BitScanReverse(&index, static_cast(u)); return uint_fast8_t(31 - index); } inline uint_fast8_t CountTrailingZeroes32(uint32_t u) { unsigned long index; _BitScanForward(&index, static_cast(u)); return uint_fast8_t(index); } inline uint_fast8_t CountPopulation32(uint32_t u) { uint32_t sum2 = (u & 0x55555555) + ((u & 0xaaaaaaaa) >> 1); uint32_t sum4 = (sum2 & 0x33333333) + ((sum2 & 0xcccccccc) >> 2); uint32_t sum8 = (sum4 & 0x0f0f0f0f) + ((sum4 & 0xf0f0f0f0) >> 4); uint32_t sum16 = (sum8 & 0x00ff00ff) + ((sum8 & 0xff00ff00) >> 8); return sum16; } inline uint_fast8_t CountLeadingZeroes64(uint64_t u) { # if defined(MOZ_BITSCAN_WINDOWS64) unsigned long index; _BitScanReverse64(&index, static_cast(u)); return uint_fast8_t(63 - index); # else uint32_t hi = uint32_t(u >> 32); if (hi != 0) return CountLeadingZeroes32(hi); return 32 + CountLeadingZeroes32(uint32_t(u)); # endif } inline uint_fast8_t CountTrailingZeroes64(uint64_t u) { # if defined(MOZ_BITSCAN_WINDOWS64) unsigned long index; _BitScanForward64(&index, static_cast(u)); return uint_fast8_t(index); # else uint32_t lo = uint32_t(u); if (lo != 0) return CountTrailingZeroes32(lo); return 32 + CountTrailingZeroes32(uint32_t(u >> 32)); # endif } # ifdef MOZ_HAVE_BITSCAN64 # undef MOZ_HAVE_BITSCAN64 # endif #elif defined(__clang__) || defined(__GNUC__) # if defined(__clang__) # if !__has_builtin(__builtin_ctz) || !__has_builtin(__builtin_clz) # error "A clang providing __builtin_c[lt]z is required to build" # endif # else // gcc has had __builtin_clz and friends since 3.4: no need to check. # endif inline uint_fast8_t CountLeadingZeroes32(uint32_t u) { return __builtin_clz(u); } inline uint_fast8_t CountTrailingZeroes32(uint32_t u) { return __builtin_ctz(u); } inline uint_fast8_t CountPopulation32(uint32_t u) { return __builtin_popcount(u); } inline uint_fast8_t CountLeadingZeroes64(uint64_t u) { return __builtin_clzll(u); } inline uint_fast8_t CountTrailingZeroes64(uint64_t u) { return __builtin_ctzll(u); } #else # error "Implement these!" inline uint_fast8_t CountLeadingZeroes32(uint32_t u) MOZ_DELETE; inline uint_fast8_t CountTrailingZeroes32(uint32_t u) MOZ_DELETE; inline uint_fast8_t CountPopulation32(uint32_t u) MOZ_DELETE; inline uint_fast8_t CountLeadingZeroes64(uint64_t u) MOZ_DELETE; inline uint_fast8_t CountTrailingZeroes64(uint64_t u) MOZ_DELETE; #endif } // namespace detail /** * Compute the number of high-order zero bits in the NON-ZERO number |u|. That * is, looking at the bitwise representation of the number, with the highest- * valued bits at the start, return the number of zeroes before the first one * is observed. * * CountLeadingZeroes32(0xF0FF1000) is 0; * CountLeadingZeroes32(0x7F8F0001) is 1; * CountLeadingZeroes32(0x3FFF0100) is 2; * CountLeadingZeroes32(0x1FF50010) is 3; and so on. */ inline uint_fast8_t CountLeadingZeroes32(uint32_t u) { MOZ_ASSERT(u != 0); return detail::CountLeadingZeroes32(u); } /** * Compute the number of low-order zero bits in the NON-ZERO number |u|. That * is, looking at the bitwise representation of the number, with the lowest- * valued bits at the start, return the number of zeroes before the first one * is observed. * * CountTrailingZeroes32(0x0100FFFF) is 0; * CountTrailingZeroes32(0x7000FFFE) is 1; * CountTrailingZeroes32(0x0080FFFC) is 2; * CountTrailingZeroes32(0x0080FFF8) is 3; and so on. */ inline uint_fast8_t CountTrailingZeroes32(uint32_t u) { MOZ_ASSERT(u != 0); return detail::CountTrailingZeroes32(u); } /** * Compute the number of one bits in the number |u|, */ inline uint_fast8_t CountPopulation32(uint32_t u) { return detail::CountPopulation32(u); } /** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */ inline uint_fast8_t CountLeadingZeroes64(uint64_t u) { MOZ_ASSERT(u != 0); return detail::CountLeadingZeroes64(u); } /** Analogous to CountTrailingZeroes32, but for 64-bit numbers. */ inline uint_fast8_t CountTrailingZeroes64(uint64_t u) { MOZ_ASSERT(u != 0); return detail::CountTrailingZeroes64(u); } namespace detail { template class CeilingLog2; template class CeilingLog2 { public: static uint_fast8_t compute(const T t) { // Check for <= 1 to avoid the == 0 undefined case. return t <= 1 ? 0 : 32 - CountLeadingZeroes32(t - 1); } }; template class CeilingLog2 { public: static uint_fast8_t compute(const T t) { // Check for <= 1 to avoid the == 0 undefined case. return t <= 1 ? 0 : 64 - CountLeadingZeroes64(t - 1); } }; } // namespace detail /** * Compute the log of the least power of 2 greater than or equal to |t|. * * 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 inline uint_fast8_t CeilingLog2(const T t) { return detail::CeilingLog2::compute(t); } /** A CeilingLog2 variant that accepts only size_t. */ inline uint_fast8_t CeilingLog2Size(size_t n) { return CeilingLog2(n); } namespace detail { template class FloorLog2; template class FloorLog2 { public: static uint_fast8_t compute(const T t) { return 31 - CountLeadingZeroes32(t | 1); } }; template class FloorLog2 { public: static uint_fast8_t compute(const T t) { return 63 - CountLeadingZeroes64(t | 1); } }; } // namespace detail /** * Compute the log of the greatest power of 2 less than or equal to |t|. * * FloorLog2(0..1) is 0; * FloorLog2(2..3) is 1; * FloorLog2(4..7) is 2; * FloorLog2(8..15) is 3; and so on. */ template inline uint_fast8_t FloorLog2(const T t) { return detail::FloorLog2::compute(t); } /** A FloorLog2 variant that accepts only size_t. */ inline uint_fast8_t FloorLog2Size(size_t n) { return FloorLog2(n); } /* * 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|. */ inline size_t RoundUpPow2(size_t x) { MOZ_ASSERT(x <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)), "can't round up -- will overflow!"); return size_t(1) << CeilingLog2(x); } } /* namespace mozilla */ #endif /* mozilla_MathAlgorithms_h */