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