]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tools: implement functions to look up the nth bit set in a mask
authorWilly Tarreau <w@1wt.eu>
Tue, 26 Feb 2019 08:56:22 +0000 (09:56 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 27 Feb 2019 13:27:07 +0000 (14:27 +0100)
Function mask_find_rank_bit() returns the bit position in mask <m> of
the nth bit set of rank <r>, between 0 and LONGBITS-1 included, starting
from the left. For example ranks 0,1,2,3 for mask 0x55 will be 6, 4, 2
and 0 respectively. This algorithm is based on a popcount variant and
is described here : https://graphics.stanford.edu/~seander/bithacks.html.

include/common/standard.h
src/standard.c

index 6105eec2df262358f748c3173001994ff2795227..87d5f88bb3c101b3e67abf078ef554f3b7a76555 100644 (file)
@@ -938,6 +938,20 @@ static inline unsigned int __full_hash(unsigned int a)
        return a * 3221225473U;
 }
 
+/* Return the bit position in mask <m> of the nth bit set of rank <r>, between
+ * 0 and LONGBITS-1 included, starting from the left. For example ranks 0,1,2,3
+ * for mask 0x55 will be 6, 4, 2 and 0 respectively. This algorithm is based on
+ * a popcount variant and is described here :
+ *   https://graphics.stanford.edu/~seander/bithacks.html
+ */
+unsigned int mask_find_rank_bit(unsigned int r, unsigned long m);
+unsigned int mask_find_rank_bit_fast(unsigned int r, unsigned long m,
+                                     unsigned long a, unsigned long b,
+                                     unsigned long c, unsigned long d);
+void mask_prep_rank_map(unsigned long m,
+                        unsigned long *a, unsigned long *b,
+                        unsigned long *c, unsigned long *d);
+
 /* sets the address family to AF_UNSPEC so that is_addr() does not match */
 static inline void clear_addr(struct sockaddr_storage *addr)
 {
index 24e014aa85c6016cbbb7ca02791b20fc6332b4a3..2bc4405fb721dff0e085a55e0e1ad29605ac7346 100644 (file)
@@ -2650,6 +2650,93 @@ unsigned int full_hash(unsigned int a)
        return __full_hash(a);
 }
 
+/* Return the bit position in mask <m> of the nth bit set of rank <r>, between
+ * 0 and LONGBITS-1 included, starting from the left. For example ranks 0,1,2,3
+ * for mask 0x55 will be 6, 4, 2 and 0 respectively. This algorithm is based on
+ * a popcount variant and is described here :
+ *   https://graphics.stanford.edu/~seander/bithacks.html
+ */
+unsigned int mask_find_rank_bit(unsigned int r, unsigned long m)
+{
+       unsigned long a, b, c, d;
+       unsigned int s;
+       unsigned int t;
+
+       a =  m - ((m >> 1) & ~0UL/3);
+       b = (a & ~0UL/5) + ((a >> 2) & ~0UL/5);
+       c = (b + (b >> 4)) & ~0UL/0x11;
+       d = (c + (c >> 8)) & ~0UL/0x101;
+
+       r++; // make r be 1..64
+
+       t = 0;
+       s = LONGBITS;
+       if (s > 32) {
+               t = (d >> 32) + (d >> 48);
+               s -= ((t - r) & 256) >> 3; r -= (t & ((t - r) >> 8));
+       }
+
+       t  = (d >> (s - 16)) & 0xff;
+       s -= ((t - r) & 256) >> 4; r -= (t & ((t - r) >> 8));
+       t  = (c >> (s - 8)) & 0xf;
+       s -= ((t - r) & 256) >> 5; r -= (t & ((t - r) >> 8));
+       t  = (b >> (s - 4)) & 0x7;
+       s -= ((t - r) & 256) >> 6; r -= (t & ((t - r) >> 8));
+       t  = (a >> (s - 2)) & 0x3;
+       s -= ((t - r) & 256) >> 7; r -= (t & ((t - r) >> 8));
+       t  = (m >> (s - 1)) & 0x1;
+       s -= ((t - r) & 256) >> 8;
+
+       return s - 1;
+}
+
+/* Same as mask_find_rank_bit() above but makes use of pre-computed bitmaps
+ * based on <m>, in <a..d>. These ones must be updated whenever <m> changes
+ * using mask_prep_rank_map() below.
+ */
+unsigned int mask_find_rank_bit_fast(unsigned int r, unsigned long m,
+                                     unsigned long a, unsigned long b,
+                                     unsigned long c, unsigned long d)
+{
+       unsigned int s;
+       unsigned int t;
+
+       r++; // make r be 1..64
+
+       t = 0;
+       s = LONGBITS;
+       if (s > 32) {
+               t = (d >> 32) + (d >> 48);
+               s -= ((t - r) & 256) >> 3; r -= (t & ((t - r) >> 8));
+       }
+
+       t  = (d >> (s - 16)) & 0xff;
+       s -= ((t - r) & 256) >> 4; r -= (t & ((t - r) >> 8));
+       t  = (c >> (s - 8)) & 0xf;
+       s -= ((t - r) & 256) >> 5; r -= (t & ((t - r) >> 8));
+       t  = (b >> (s - 4)) & 0x7;
+       s -= ((t - r) & 256) >> 6; r -= (t & ((t - r) >> 8));
+       t  = (a >> (s - 2)) & 0x3;
+       s -= ((t - r) & 256) >> 7; r -= (t & ((t - r) >> 8));
+       t  = (m >> (s - 1)) & 0x1;
+       s -= ((t - r) & 256) >> 8;
+
+       return s - 1;
+}
+
+/* Prepare the bitmaps used by the fast implementation of the find_rank_bit()
+ * above.
+ */
+void mask_prep_rank_map(unsigned long m,
+                        unsigned long *a, unsigned long *b,
+                        unsigned long *c, unsigned long *d)
+{
+       *a =  m - ((m >> 1) & ~0UL/3);
+       *b = (*a & ~0UL/5) + ((*a >> 2) & ~0UL/5);
+       *c = (*b + (*b >> 4)) & ~0UL/0x11;
+       *d = (*c + (*c >> 8)) & ~0UL/0x101;
+}
+
 /* Return non-zero if IPv4 address is part of the network,
  * otherwise zero. Note that <addr> may not necessarily be aligned
  * while the two other ones must.