From 420f06bef33604a08a0be3e50d45f1be2727e950 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 26 Apr 2024 11:15:51 -0700 Subject: [PATCH] implement 16 and 8 bits clz, ctz and popcount This is needed for utils::bitset8 and bitset16. --- libs/utils/include/utils/algorithm.h | 82 ++++++++++++++++++++++++---- libs/utils/test/test_algorithm.cpp | 26 +++++++++ 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/libs/utils/include/utils/algorithm.h b/libs/utils/include/utils/algorithm.h index ea5ca44fd5..7a747b84ce 100644 --- a/libs/utils/include/utils/algorithm.h +++ b/libs/utils/include/utils/algorithm.h @@ -22,6 +22,7 @@ #include // for std::enable_if #include +#include #include namespace utils { @@ -43,9 +44,15 @@ constexpr inline T clz(T x) noexcept { static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits"); x |= (x >> 1u); x |= (x >> 2u); - x |= (x >> 4u); - x |= (x >> 8u); - x |= (x >> 16u); + if constexpr (sizeof(T) * CHAR_BIT >= 8) { // just to silence compiler warning + x |= (x >> 4u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { // just to silence compiler warning + x |= (x >> 8u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 32) { // just to silence compiler warning + x |= (x >> 16u); + } if constexpr (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning x |= (x >> 32u); } @@ -67,11 +74,15 @@ constexpr inline T ctz(T x) noexcept { x &= -x; #endif if (x) c--; - if (sizeof(T) * CHAR_BIT >= 64) { + if constexpr (sizeof(T) * CHAR_BIT >= 64) { if (x & T(0x00000000FFFFFFFF)) c -= 32; } - if (x & T(0x0000FFFF0000FFFF)) c -= 16; - if (x & T(0x00FF00FF00FF00FF)) c -= 8; + if constexpr (sizeof(T) * CHAR_BIT >= 32) { + if (x & T(0x0000FFFF0000FFFF)) c -= 16; + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { + if (x & T(0x00FF00FF00FF00FF)) c -= 8; + } if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4; if (x & T(0x3333333333333333)) c -= 2; if (x & T(0x5555555555555555)) c -= 1; @@ -80,6 +91,24 @@ constexpr inline T ctz(T x) noexcept { } // namespace details +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned char x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 24; +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned short x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 16; +#else + return details::clz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept { #if __has_builtin(__builtin_clz) @@ -107,6 +136,24 @@ 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 char x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned short x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept { #if __has_builtin(__builtin_ctz) @@ -134,6 +181,24 @@ unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned char x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned short x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept { #if __has_builtin(__builtin_popcount) @@ -161,11 +226,6 @@ unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept { #endif } -constexpr inline UTILS_PUBLIC UTILS_PURE -uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept { - return (uint8_t)popcount((unsigned int)x); -} - template::value && std::is_unsigned::value>> constexpr inline UTILS_PUBLIC UTILS_PURE diff --git a/libs/utils/test/test_algorithm.cpp b/libs/utils/test/test_algorithm.cpp index fa36aff0dc..83dcc7b65b 100644 --- a/libs/utils/test/test_algorithm.cpp +++ b/libs/utils/test/test_algorithm.cpp @@ -20,6 +20,8 @@ #include +#include + using namespace utils; template @@ -58,6 +60,20 @@ TEST(AlgorithmTest, clz) { EXPECT_EQ(j, details::clz(i|1)); EXPECT_EQ(j, count_leading_zeros(i)); } + for (uint16_t i = 1, j = 15; j < 16; i *= 2, j--) { + EXPECT_EQ(j, clz(i)); + EXPECT_EQ(j, clz(uint16_t(i|1))); + EXPECT_EQ(j, details::clz(i)); + EXPECT_EQ(j, details::clz(uint16_t(i|1))); + EXPECT_EQ(j, count_leading_zeros(i)); + } + for (uint8_t i = 1, j = 7; j < 8; i *= 2, j--) { + EXPECT_EQ(j, clz(i)); + EXPECT_EQ(j, clz(uint8_t(i|1))); + EXPECT_EQ(j, details::clz(i)); + EXPECT_EQ(j, details::clz(uint8_t(i|1))); + EXPECT_EQ(j, count_leading_zeros(i)); + } } TEST(AlgorithmTest, details_ctz) { @@ -80,6 +96,16 @@ TEST(AlgorithmTest, ctz) { EXPECT_EQ(j, details::ctz(i)); EXPECT_EQ(j, count_trailing_zeros(i)); } + for (uint16_t i = 1, j = 0; j < 16; i *= 2, j++) { + EXPECT_EQ(j, ctz(i)); + EXPECT_EQ(j, details::ctz(i)); + EXPECT_EQ(j, count_trailing_zeros(i)); + } + for (uint8_t i = 1, j = 0; j < 8; 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) {