]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Bitops: u64_log2() and encoding variable-length bitstrings into u64
authorMaria Matejka <mq@ucw.cz>
Mon, 8 Apr 2019 13:07:32 +0000 (15:07 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 8 Apr 2019 13:08:30 +0000 (15:08 +0200)
lib/bitops.c
lib/bitops.h
lib/bitops_test.c
test/bt-utils.c
test/bt-utils.h

index efb8710e652c8faff23e4c1e9b2314a0817e5d75..9554b7edc8ec77be2486e0c5e746df3e7e2c68e1 100644 (file)
@@ -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;
+}
index af648c268e1e8fc9d9daeb63145985188af39d78..8b107115959571443d894f30e79560cb410812a1 100644 (file)
  *                     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
index f816b9d1ef208fcccad5914510d240d92bce9572..8bc8803dbec626a107a4991c4d6a6a8ae33e30fb 100644 (file)
@@ -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();
 }
index 7653abf64f0ae35abf433f6d79767ffabc45f1a3..388dc73f3e35abfd939e7b3d258694c5ffff3a69 100644 (file)
@@ -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;
index 13d267cc1c1710a38184934fc1b257269db935a8..0bb8c14fb88d0954a67b519a6afc9b17d1faa048 100644 (file)
@@ -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);