From: Maria Matejka Date: Mon, 8 Apr 2019 13:07:32 +0000 (+0200) Subject: Bitops: u64_log2() and encoding variable-length bitstrings into u64 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6cc547a13ddc39616be129b417ee7545efed3907;p=thirdparty%2Fbird.git Bitops: u64_log2() and encoding variable-length bitstrings into u64 --- diff --git a/lib/bitops.c b/lib/bitops.c index efb8710e6..9554b7edc 100644 --- a/lib/bitops.c +++ b/lib/bitops.c @@ -68,3 +68,15 @@ u32_log2(u32 v) return r; } +u64 +u64_log2(u64 v) +{ + u64 r, shift; + r = (v > 0xFFFFFFFFLL) << 5; v >>= r; + shift = (v > 0xFFFF ) << 4; v >>= shift; r |= shift; + shift = (v > 0xFF ) << 3; v >>= shift; r |= shift; + shift = (v > 0xF ) << 2; v >>= shift; r |= shift; + shift = (v > 0x3 ) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; +} diff --git a/lib/bitops.h b/lib/bitops.h index af648c268..8b1071159 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -18,15 +18,53 @@ * from the left and the rest filled with zeroes. * E.g., u32_mkmask(5) = 0xf8000000. * u32_masklen Inverse operation to u32_mkmask, -1 if not a bitmask. + * + * u32_log2 + * u64_log2 Find the first 1 in the number + * + * u32_hash Compute a common hash + * + * u64_var_encode Encode a variable-length bitstring into fixed-length u64 + * u64_var_decode Decode the bitstring */ u32 u32_mkmask(uint n); uint u32_masklen(u32 x); u32 u32_log2(u32 v); +u64 u64_log2(u64 v); static inline u32 u32_hash(u32 v) { return v * 2902958171u; } static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); } +static inline u64 u64_var_encode(u64 data, uint padlen) +{ + ASSERT(padlen > 0); + + /* Append the other bit than the last */ + if (data & 1) + return data << padlen; + else + return (data << padlen) | ((1ULL << padlen) - 1); +} + +static inline u64 u64_var_decode(u64 enc, uint *padlen) +{ + /* If enc is ....|100..00, then cpl is ....|011..11 + * If enc is ....|011..11, then cpl is ....|100..00 + * + * In both cases, enc ^ cpl is then 0...0|111..11 + * so u64_log2((enc ^ cpl) + 1) is the number of bits to shift right. + * */ + u64 cpl = (enc & 1) ? (enc + 1) : (enc - 1); + if ((~enc == 0) || (~cpl == 0)) { + *padlen = 64; + return 0; + } else { + *padlen = u64_log2(enc ^ cpl); + return enc >> *padlen; + } +} + #endif diff --git a/lib/bitops_test.c b/lib/bitops_test.c index f816b9d1e..8bc8803db 100644 --- a/lib/bitops_test.c +++ b/lib/bitops_test.c @@ -82,11 +82,14 @@ t_masklen(void) } static void -check_log2(u32 n) +check_log2(u64 n) { - u32 log = u32_log2(n); - u32 low = bt_naive_pow(2, log); - u32 high = bt_naive_pow(2, log+1); + u64 log = u64_log2(n); + u64 low = bt_naive_pow(2, log); + u64 high = bt_naive_pow(2, log+1); + + if (n <= 0xffffffff) + bt_assert(u32_log2(n) == log); bt_assert_msg(n >= low && n < high, "u32_log2(%u) = %u, %u should be in the range <%u, %u)", @@ -101,15 +104,47 @@ t_log2(void) for (i = 0; i < 31; i++) bt_assert(u32_log2(bt_naive_pow(2, i+1)) == i+1); + for (i = 0; i < 63; i++) + bt_assert(u64_log2(bt_naive_pow(2, i+1)) == i+1); + for (i = 1; i < MAX_NUM; i++) check_log2(i); for (i = 1; i < MAX_NUM; i++) - check_log2(((u32) bt_random()) % 0x0fffffff); + check_log2((unsigned long int) bt_random()); return 1; } +static void +var_enc_dec(u64 data, uint padlen) +{ + uint olen = ~0; + u64 enc = u64_var_encode(data, padlen); + u64 odata = u64_var_decode(enc, &olen); + bt_assert_msg( + (odata == data) && (olen == padlen), + "u64_var_encode(0x%llx, %u) == 0x%llx; u64_var_decode(0x%llx, %u) == 0x%llx", + data, padlen, enc, enc, olen, odata + ); +} + +static int +t_var(void) +{ + for (uint i = 0; i < 63; i++) + for (uint j = 1; j+i < 64; j++) { + var_enc_dec(1ULL << i, j); + var_enc_dec((1ULL << i) - 1, j); + var_enc_dec(((unsigned long int) bt_random()) & ((1ULL << (64-j)) - 1), j); + } + + return 1; +} + + + + int main(int argc, char *argv[]) { @@ -118,6 +153,7 @@ main(int argc, char *argv[]) bt_test_suite(t_mkmask, "u32_mkmask()"); bt_test_suite(t_masklen, "u32_masklen()"); bt_test_suite(t_log2, "u32_log2()"); + bt_test_suite(t_var, "u64_var_(en|de)code()"); return bt_exit_value(); } diff --git a/test/bt-utils.c b/test/bt-utils.c index 7653abf64..388dc73f3 100644 --- a/test/bt-utils.c +++ b/test/bt-utils.c @@ -195,10 +195,10 @@ bt_config_file_parse(const char *filepath) /* * Returns @base raised to the power of @power. */ -uint +u64 bt_naive_pow(uint base, uint power) { - uint result = 1; + u64 result = 1; uint i; for (i = 0; i < power; i++) result *= base; diff --git a/test/bt-utils.h b/test/bt-utils.h index 13d267cc1..0bb8c14fb 100644 --- a/test/bt-utils.h +++ b/test/bt-utils.h @@ -24,7 +24,7 @@ #define BT_CONFIG_PARSE_STATIC_PROTO "protocol static { ipv4; } \n" #define BT_CONFIG_SIMPLE BT_CONFIG_PARSE_ROUTER_ID BT_CONFIG_PARSE_STATIC_PROTO -uint bt_naive_pow(uint base, uint power); +u64 bt_naive_pow(uint base, uint power); void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size); void bt_bird_init(void);