From: Jarno Rajahalme Date: Tue, 3 Dec 2013 21:41:41 +0000 (-0800) Subject: lib/util: Add clz32() and clz64(). X-Git-Tag: v2.1.0~233 X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=8c9479030386430afd781bba4b926b177ffeac63;p=cascardo%2Fovs.git lib/util: Add clz32() and clz64(). Count leading zeroes using builtin if available. Make log_2_floor() use raw_clz() and inline log_2_floor() and log_2_ceil(). Signed-off-by: Jarno Rajahalme Acked-by: Ben Pfaff --- diff --git a/lib/util.c b/lib/util.c index 53c38493b..76f4cd6a0 100644 --- a/lib/util.c +++ b/lib/util.c @@ -848,50 +848,11 @@ english_list_delimiter(size_t index, size_t total) : " and "); } -/* Given a 32 bit word 'n', calculates floor(log_2('n')). This is equivalent - * to finding the bit position of the most significant one bit in 'n'. It is - * an error to call this function with 'n' == 0. */ -int -log_2_floor(uint32_t n) -{ - ovs_assert(n); - -#if !defined(UINT_MAX) || !defined(UINT32_MAX) -#error "Someone screwed up the #includes." -#elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX - return 31 - __builtin_clz(n); -#else - { - int log = 0; - -#define BIN_SEARCH_STEP(BITS) \ - if (n >= (1 << BITS)) { \ - log += BITS; \ - n >>= BITS; \ - } - BIN_SEARCH_STEP(16); - BIN_SEARCH_STEP(8); - BIN_SEARCH_STEP(4); - BIN_SEARCH_STEP(2); - BIN_SEARCH_STEP(1); -#undef BIN_SEARCH_STEP - return log; - } -#endif -} - -/* Given a 32 bit word 'n', calculates ceil(log_2('n')). It is an error to - * call this function with 'n' == 0. */ -int -log_2_ceil(uint32_t n) -{ - return log_2_floor(n) + !is_pow2(n); -} - /* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ #if __GNUC__ >= 4 /* Defined inline in util.h. */ #else +/* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ int raw_ctz(uint64_t n) { @@ -914,6 +875,30 @@ raw_ctz(uint64_t n) return count; } + +/* Returns the number of leading 0-bits in 'n'. Undefined if 'n' == 0. */ +int +raw_clz64(uint64_t n) +{ + uint64_t k; + int count = 63; + +#define CLZ_STEP(X) \ + k = n >> (X); \ + if (k) { \ + count -= X; \ + n = k; \ + } + CLZ_STEP(32); + CLZ_STEP(16); + CLZ_STEP(8); + CLZ_STEP(4); + CLZ_STEP(2); + CLZ_STEP(1); +#undef CLZ_STEP + + return count; +} #endif /* Returns the number of 1-bits in 'x', between 0 and 32 inclusive. */ diff --git a/lib/util.h b/lib/util.h index 4f8a29100..5c23962c8 100644 --- a/lib/util.h +++ b/lib/util.h @@ -302,10 +302,6 @@ void ignore(bool x OVS_UNUSED); /* Bitwise tests. */ -int log_2_floor(uint32_t); -int log_2_ceil(uint32_t); -unsigned int count_1bits(uint64_t); - /* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ #if __GNUC__ >= 4 static inline int @@ -318,9 +314,16 @@ raw_ctz(uint64_t n) ? __builtin_ctz(n) : __builtin_ctzll(n)); } + +static inline int +raw_clz64(uint64_t n) +{ + return __builtin_clzll(n); +} #else /* Defined in util.c. */ int raw_ctz(uint64_t n); +int raw_clz64(uint64_t n); #endif /* Returns the number of trailing 0-bits in 'n', or 32 if 'n' is 0. */ @@ -337,6 +340,39 @@ ctz64(uint64_t n) return n ? raw_ctz(n) : 64; } +/* Returns the number of leading 0-bits in 'n', or 32 if 'n' is 0. */ +static inline int +clz32(uint32_t n) +{ + return n ? raw_clz64(n) - 32 : 32; +} + +/* Returns the number of leading 0-bits in 'n', or 64 if 'n' is 0. */ +static inline int +clz64(uint64_t n) +{ + return n ? raw_clz64(n) : 64; +} + +/* Given a word 'n', calculates floor(log_2('n')). This is equivalent + * to finding the bit position of the most significant one bit in 'n'. It is + * an error to call this function with 'n' == 0. */ +static inline int +log_2_floor(uint64_t n) +{ + return 63 - raw_clz64(n); +} + +/* Given a word 'n', calculates ceil(log_2('n')). It is an error to + * call this function with 'n' == 0. */ +static inline int +log_2_ceil(uint64_t n) +{ + return log_2_floor(n) + !is_pow2(n); +} + +unsigned int count_1bits(uint64_t); + /* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x' * is 0. */ static inline uintmax_t diff --git a/tests/library.at b/tests/library.at index 6e28573ea..57cdd6c0d 100644 --- a/tests/library.at +++ b/tests/library.at @@ -112,6 +112,7 @@ AT_CLEANUP m4_foreach( [testname], [[ctz], + [clz], [round_up_pow2], [round_down_pow2], [count_1bits], diff --git a/tests/test-util.c b/tests/test-util.c index 8dbc98bfd..9152562cb 100644 --- a/tests/test-util.c +++ b/tests/test-util.c @@ -113,6 +113,58 @@ test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) check_ctz64(0, 64); } +static void +check_clz32(uint32_t x, int n) +{ + if (clz32(x) != n) { + fprintf(stderr, "clz32(%"PRIu32") is %d but should be %d\n", + x, clz32(x), n); + abort(); + } +} + +static void +check_clz64(uint64_t x, int n) +{ + if (clz64(x) != n) { + fprintf(stderr, "clz64(%"PRIu64") is %d but should be %d\n", + x, clz64(x), n); + abort(); + } +} + +static void +test_clz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + int n; + + for (n = 0; n < 32; n++) { + /* Check minimum x such that f(x) == n. */ + check_clz32((1u << 31) >> n, n); + + /* Check maximum x such that f(x) == n. */ + check_clz32(UINT32_MAX >> n, n); + + /* Check a random value in the middle. */ + check_clz32((random_uint32() | 1u << 31) >> n, n); + } + + for (n = 0; n < 64; n++) { + /* Check minimum x such that f(x) == n. */ + check_clz64((UINT64_C(1) << 63) >> n, n); + + /* Check maximum x such that f(x) == n. */ + check_clz64(UINT64_MAX >> n, n); + + /* Check a random value in the middle. */ + check_clz64((random_uint64() | UINT64_C(1) << 63) >> n, n); + } + + /* Check clz(0). */ + check_clz32(0, 32); + check_clz64(0, 64); +} + /* Returns a random number in the range 'min'...'max' inclusive. */ static uint32_t random_in_range(uint32_t min, uint32_t max) @@ -964,6 +1016,7 @@ test_ovs_scan(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) static const struct command commands[] = { {"ctz", 0, 0, test_ctz}, + {"clz", 0, 0, test_clz}, {"round_up_pow2", 0, 0, test_round_up_pow2}, {"round_down_pow2", 0, 0, test_round_down_pow2}, {"count_1bits", 0, 0, test_count_1bits},