0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-23 02:01:55 +00:00

Bitops: u64_log2() and encoding variable-length bitstrings into u64

This commit is contained in:
Maria Matejka 2019-04-08 15:07:32 +02:00
parent 3a22a6e858
commit 6cc547a13d
5 changed files with 94 additions and 8 deletions

View File

@ -68,3 +68,15 @@ u32_log2(u32 v)
return r; 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;
}

View File

@ -18,15 +18,53 @@
* from the left and the rest filled with zeroes. * from the left and the rest filled with zeroes.
* E.g., u32_mkmask(5) = 0xf8000000. * E.g., u32_mkmask(5) = 0xf8000000.
* u32_masklen Inverse operation to u32_mkmask, -1 if not a bitmask. * 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); u32 u32_mkmask(uint n);
uint u32_masklen(u32 x); uint u32_masklen(u32 x);
u32 u32_log2(u32 v); u32 u32_log2(u32 v);
u64 u64_log2(u64 v);
static inline u32 u32_hash(u32 v) { return v * 2902958171u; } static inline u32 u32_hash(u32 v) { return v * 2902958171u; }
static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); } 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 #endif

View File

@ -82,11 +82,14 @@ t_masklen(void)
} }
static void static void
check_log2(u32 n) check_log2(u64 n)
{ {
u32 log = u32_log2(n); u64 log = u64_log2(n);
u32 low = bt_naive_pow(2, log); u64 low = bt_naive_pow(2, log);
u32 high = bt_naive_pow(2, log+1); u64 high = bt_naive_pow(2, log+1);
if (n <= 0xffffffff)
bt_assert(u32_log2(n) == log);
bt_assert_msg(n >= low && n < high, bt_assert_msg(n >= low && n < high,
"u32_log2(%u) = %u, %u should be in the range <%u, %u)", "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++) for (i = 0; i < 31; i++)
bt_assert(u32_log2(bt_naive_pow(2, i+1)) == i+1); 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++) for (i = 1; i < MAX_NUM; i++)
check_log2(i); check_log2(i);
for (i = 1; i < MAX_NUM; i++) for (i = 1; i < MAX_NUM; i++)
check_log2(((u32) bt_random()) % 0x0fffffff); check_log2((unsigned long int) bt_random());
return 1; 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 int
main(int argc, char *argv[]) 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_mkmask, "u32_mkmask()");
bt_test_suite(t_masklen, "u32_masklen()"); bt_test_suite(t_masklen, "u32_masklen()");
bt_test_suite(t_log2, "u32_log2()"); bt_test_suite(t_log2, "u32_log2()");
bt_test_suite(t_var, "u64_var_(en|de)code()");
return bt_exit_value(); return bt_exit_value();
} }

View File

@ -195,10 +195,10 @@ bt_config_file_parse(const char *filepath)
/* /*
* Returns @base raised to the power of @power. * Returns @base raised to the power of @power.
*/ */
uint u64
bt_naive_pow(uint base, uint power) bt_naive_pow(uint base, uint power)
{ {
uint result = 1; u64 result = 1;
uint i; uint i;
for (i = 0; i < power; i++) for (i = 0; i < power; i++)
result *= base; result *= base;

View File

@ -24,7 +24,7 @@
#define BT_CONFIG_PARSE_STATIC_PROTO "protocol static { ipv4; } \n" #define BT_CONFIG_PARSE_STATIC_PROTO "protocol static { ipv4; } \n"
#define BT_CONFIG_SIMPLE BT_CONFIG_PARSE_ROUTER_ID BT_CONFIG_PARSE_STATIC_PROTO #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_bytes_to_hex(char *buf, const byte *in_data, size_t size);
void bt_bird_init(void); void bt_bird_init(void);