Count leading zeroes using builtins if available.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/util.c        |   25 ++++++++++++++++++++++++-
 lib/util.h        |   35 +++++++++++++++++++++++++++++++++++
 tests/library.at  |    1 +
 tests/test-util.c |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 113 insertions(+), 1 deletion(-)

diff --git a/lib/util.c b/lib/util.c
index 75d443d..7b5756f 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -886,12 +886,12 @@ 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 !defined(UINT_MAX) || !defined(UINT32_MAX)
 #error "Someone screwed up the #includes."
 #elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX
 /* Defined inline in util.h. */
 #else
+/* Returns the number of trailing 0-bits in 'n'.  Undefined if 'n' == 0. */
 int
 raw_ctz(uint32_t n)
 {
@@ -913,6 +913,29 @@ raw_ctz(uint32_t n)
 
     return count;
 }
+
+/* Returns the number of leading 0-bits in 'n'.  Undefined if 'n' == 0. */
+int
+raw_clz(uint32_t n)
+{
+    unsigned int k;
+    int count = 31;
+
+#define CLZ_STEP(X)                             \
+    k = n >> (X);                               \
+    if (k) {                                    \
+        count -= X;                             \
+        n = k;                                  \
+    }
+    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 62dc3ae..bb4c2e1 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -298,9 +298,15 @@ raw_ctz(uint32_t n)
 {
     return __builtin_ctz(n);
 }
+static inline int
+raw_clz(uint32_t n)
+{
+    return __builtin_clz(n);
+}
 #else
 /* Defined in util.c. */
 int raw_ctz(uint32_t n);
+int raw_clz(uint32_t n);
 #endif
 
 #if !defined(UINT_MAX) || !defined(UINT64_MAX)
@@ -312,6 +318,11 @@ raw_ctz64(uint64_t n)
     return __builtin_ctzl(n);
 }
 static inline int
+raw_clz64(uint64_t n)
+{
+    return __builtin_clzl(n);
+}
+static inline int
 popcount64(uint64_t n)
 {
     return __builtin_popcountl(n);
@@ -323,6 +334,11 @@ raw_ctz64(uint64_t n)
     return __builtin_ctzll(n);
 }
 static inline int
+raw_clz64(uint64_t n)
+{
+    return __builtin_clzll(n);
+}
+static inline int
 popcount64(uint64_t n)
 {
     return __builtin_popcountll(n);
@@ -335,6 +351,11 @@ raw_ctz64(uint64_t n)
     return (uint32_t)n ? raw_ctz(n) : 32 + raw_ctz(n >> 32);
 }
 static inline int
+raw_clz64(uint64_t n)
+{
+    return n >> 32 ? raw_clz(n >> 32) : 32 + raw_clz(n);
+}
+static inline int
 popcount64(uint64_t n)
 {
     return popcount(n) + popcount(n >> 32);
@@ -355,6 +376,20 @@ ctz64(uint64_t n)
     return n ? raw_ctz64(n) : 64;
 }
 
+/* Returns the number of leading 0-bits in 'n', or 32 if 'n' is 0. */
+static inline int
+clz(uint32_t n)
+{
+    return n ? raw_clz(n) : 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;
+}
+
 /* 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 6d1c6da..e7f82cb 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],
    [popcount],
diff --git a/tests/test-util.c b/tests/test-util.c
index 4f5b78c..fdc2c9b 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_clz(uint32_t x, int n)
+{
+    if (clz(x) != n) {
+        fprintf(stderr, "clz(%"PRIu32") is %d but should be %d\n",
+                x, clz(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_clz((1u << 31) >> n, n);
+
+        /* Check maximum x such that f(x) == n. */
+        check_clz(UINT32_MAX >> n, n);
+
+        /* Check a random value in the middle. */
+        check_clz((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_clz(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)
@@ -462,6 +514,7 @@ test_assert(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},
     {"popcount", 0, 0, test_popcount},
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to