diff --git a/include/haproxy/intops.h b/include/haproxy/intops.h index 86b02aaf7c..34010ccd13 100644 --- a/include/haproxy/intops.h +++ b/include/haproxy/intops.h @@ -47,6 +47,7 @@ unsigned int mask_find_rank_bit_fast(unsigned int r, unsigned long m, void mask_prep_rank_map(unsigned long m, unsigned long *a, unsigned long *b, unsigned long *c, unsigned long *d); +int one_among_mask(unsigned long v, int bit); /* Multiply the two 32-bit operands and shift the 64-bit result right 32 bits. diff --git a/src/tools.c b/src/tools.c index c2ee7c953c..e77b7e13bb 100644 --- a/src/tools.c +++ b/src/tools.c @@ -3156,6 +3156,60 @@ void mask_prep_rank_map(unsigned long m, *d = (*c + (*c >> 8)) & ~0UL/0x101; } +/* Returns the position of one bit set in , starting at position , and + * searching in other halves if not found. This is intended to be used to + * report the position of one bit set among several based on a counter or a + * random generator while preserving a relatively good distribution so that + * values made of holes in the middle do not see one of the bits around the + * hole being returned much more often than the other one. It can be seen as a + * disturbed ffsl() where the initial search starts at bit . The look up + * is performed in O(logN) time for N bit words, yielding a bit among 64 in + * about 16 cycles. Its usage differs from the rank find function in that the + * bit passed doesn't need to be limited to the value's popcount, making the + * function easier to use for random picking, and twice as fast. Passing value + * 0 for makes no sense and -1 is returned in this case. + */ +int one_among_mask(unsigned long v, int bit) +{ + /* note, these masks may be produced by ~0UL/((1UL< 4) ? 5 : 4; scale >= 0; scale--) { + halfword >>= (1UL << scale); + scope |= (1UL << scale); + mirror = bit ^ (1UL << scale); + if (v & ((1UL << bit) | (1UL << mirror))) + return (v & (1UL << bit)) ? bit : mirror; + + if (!((v >> (bit & scope)) & halves[scale] & halfword)) + bit = mirror; + } + return bit; +} + /* Return non-zero if IPv4 address is part of the network, * otherwise zero. Note that may not necessarily be aligned * while the two other ones must.