diff --git a/libs/utils/include/utils/algorithm.h b/libs/utils/include/utils/algorithm.h index 4cab29199b..c0d5c5792d 100644 --- a/libs/utils/include/utils/algorithm.h +++ b/libs/utils/include/utils/algorithm.h @@ -50,6 +50,23 @@ constexpr inline T clz(T x) noexcept { } return (sizeof(T) * CHAR_BIT) - details::popcount(x); } + +template +constexpr inline T ctz(T x) noexcept { + static_assert(sizeof(T) <= sizeof(uint64_t), "details::ctz() only support up to 64 bits"); + T c = sizeof(T) * 8; + x &= -signed(x); + if (x) c--; + if (sizeof(T) * 8 > 32) { // if() only needed to quash compiler warnings + if (x & 0x00000000FFFFFFFF) c -= 32; + } + if (x & 0x0000FFFF) c -= 16; + if (x & 0x00FF00FF) c -= 8; + if (x & 0x0F0F0F0F) c -= 4; + if (x & 0x33333333) c -= 2; + if (x & 0x55555555) c -= 1; + return c; +} } // namespace details constexpr inline UTILS_PUBLIC UTILS_PURE @@ -79,6 +96,32 @@ unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long UTILS_ALWAYS_INLINE ctz(unsigned long x) noexcept { +#if __has_builtin(__builtin_ctzl) + return __builtin_ctzl(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept { +#if __has_builtin(__builtin_ctzll) + return __builtin_ctzll(x); +#else + return details::ctz(x); +#endif +} constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept { diff --git a/libs/utils/include/utils/bitset.h b/libs/utils/include/utils/bitset.h index d0bad9b6b1..a7da2acd7b 100644 --- a/libs/utils/include/utils/bitset.h +++ b/libs/utils/include/utils/bitset.h @@ -80,7 +80,7 @@ public: for (size_t i = 0; i < N; i++) { T v = storage[i]; while (v) { - T k = (BITS_PER_WORD - 1) - utils::clz(v); + T k = utils::ctz(v); v &= ~(T(1) << k); exec(size_t(k + BITS_PER_WORD * i)); } diff --git a/libs/utils/test/test_algorithm.cpp b/libs/utils/test/test_algorithm.cpp index fd4fb9d58d..3c6186716a 100644 --- a/libs/utils/test/test_algorithm.cpp +++ b/libs/utils/test/test_algorithm.cpp @@ -27,6 +27,11 @@ static inline T count_leading_zeros(T v) noexcept { return utils::clz(v); } +template +static inline T count_trailing_zeros(T v) noexcept { + return utils::ctz(v); +} + TEST(AlgorithmTest, details_clz) { for (uint64_t i = 1, j = 63; i < 64; i *= 2, j--) { EXPECT_EQ(j, details::clz(i)); @@ -55,6 +60,28 @@ TEST(AlgorithmTest, clz) { } } +TEST(AlgorithmTest, details_ctz) { + for (uint64_t i = 1, j = 0; i < 64; i *= 2, j++) { + EXPECT_EQ(j, details::ctz(i)); + } + for (uint32_t i = 1, j = 0; i < 32; i *= 2, j++) { + EXPECT_EQ(j, details::ctz(i)); + } +} + +TEST(AlgorithmTest, ctz) { + for (uint64_t i = 1, j = 0; i < 64; i *= 2, j++) { + EXPECT_EQ(j, ctz(i)); + EXPECT_EQ(j, details::ctz(i)); + EXPECT_EQ(j, count_trailing_zeros(i)); + } + for (uint32_t i = 1, j = 0; i < 32; i *= 2, j++) { + EXPECT_EQ(j, ctz(i)); + EXPECT_EQ(j, details::ctz(i)); + EXPECT_EQ(j, count_trailing_zeros(i)); + } +} + TEST(AlgorithmTest, details_popcount) { EXPECT_EQ(0, details::popcount(0u)); EXPECT_EQ(0, details::popcount(0lu));