Willy Tarreau | 798d6fc | 2022-06-21 20:28:14 +0200 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <stdint.h> |
| 4 | |
| 5 | static inline unsigned long statistical_prng() |
| 6 | { |
| 7 | static unsigned long statistical_prng_state = 2463534242U; |
| 8 | unsigned long x = statistical_prng_state; |
| 9 | |
| 10 | if (sizeof(long) <= 4) { |
| 11 | x ^= x << 13; |
| 12 | x ^= x >> 17; |
| 13 | x ^= x << 5; |
| 14 | } else { |
| 15 | x ^= x << 13; |
| 16 | x ^= x >> 7; |
| 17 | x ^= x << 17; |
| 18 | } |
| 19 | return statistical_prng_state = x; |
| 20 | } |
| 21 | |
| 22 | /* returns the position of one bit set in <v>, starting at position <bit>, and |
| 23 | * searching in other halves if not found. This is intended to be used to |
| 24 | * report the position of one bit set among several based on a counter or a |
| 25 | * random generator while preserving a relatively good distribution so that |
| 26 | * values made of holes in the middle do not see one of the bits around the |
| 27 | * hole being returned much more often than the other one. It can be seen as a |
| 28 | * disturbed ffsl() where the initial search starts at bit <bit>. The look up |
| 29 | * is performed in O(logN) time for N bit words, yielding a bit among 64 in |
| 30 | * about 16 cycles. Passing value 0 for <v> makes no sense and -1 is returned |
| 31 | * in this case. |
| 32 | */ |
| 33 | int one_among(unsigned long v, int bit) |
| 34 | { |
| 35 | /* note, these masks may be produced by ~0UL/((1UL<<scale)+1) but |
| 36 | * that's more expensive. |
| 37 | */ |
| 38 | static const unsigned long halves[] = { |
| 39 | (unsigned long)0x5555555555555555ULL, |
| 40 | (unsigned long)0x3333333333333333ULL, |
| 41 | (unsigned long)0x0F0F0F0F0F0F0F0FULL, |
| 42 | (unsigned long)0x00FF00FF00FF00FFULL, |
| 43 | (unsigned long)0x0000FFFF0000FFFFULL, |
| 44 | (unsigned long)0x00000000FFFFFFFFULL |
| 45 | }; |
| 46 | unsigned long halfword = ~0UL; |
| 47 | int scope = 0; |
| 48 | int mirror; |
| 49 | int scale; |
| 50 | |
| 51 | if (!v) |
| 52 | return -1; |
| 53 | |
| 54 | /* we check if the exact bit is set or if it's present in a mirror |
| 55 | * position based on the current scale we're checking, in which case |
| 56 | * it's returned with its current (or mirrored) value. Otherwise we'll |
| 57 | * make sure there's at least one bit in the half we're in, and will |
| 58 | * scale down to a smaller scope and try again, until we find the |
| 59 | * closest bit. |
| 60 | */ |
| 61 | for (scale = (sizeof(long) > 4) ? 5 : 4; scale >= 0; scale--) { |
| 62 | halfword >>= (1UL << scale); |
| 63 | scope |= (1UL << scale); |
| 64 | mirror = bit ^ (1UL << scale); |
| 65 | if (v & ((1UL << bit) | (1UL << mirror))) |
| 66 | return (v & (1UL << bit)) ? bit : mirror; |
| 67 | |
| 68 | if (!((v >> (bit & scope)) & halves[scale] & halfword)) |
| 69 | bit = mirror; |
| 70 | } |
| 71 | return bit; |
| 72 | } |
| 73 | |
| 74 | int main(int argc, char **argv) |
| 75 | { |
| 76 | unsigned long mask; |
| 77 | int bit; |
| 78 | |
| 79 | if (argc < 2) { |
| 80 | unsigned long long tests = 0; |
| 81 | int ret; |
| 82 | |
| 83 | while (1) { |
| 84 | mask = statistical_prng(); // note: cannot be zero |
| 85 | bit = statistical_prng() % (sizeof(long) * 8); |
| 86 | ret = one_among(mask, bit); |
| 87 | if (ret < 0 || !((mask >> ret) & 1)) |
| 88 | printf("###ERR### mask=%#lx bit=%d ret=%d\n", mask, bit, ret); |
| 89 | if (!(tests & 0xffffff)) |
| 90 | printf("count=%Ld mask=%lx bit=%d ret=%d\n", tests, mask, bit, ret); |
| 91 | tests++; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | mask = atol(argv[1]); |
| 96 | |
| 97 | if (argc < 3) { |
| 98 | for (bit = 0; bit < 8*sizeof(long); bit++) |
| 99 | printf("v %#x bit %d best %d\n", mask, bit, one_among(mask, bit)); |
| 100 | } else { |
| 101 | bit = atoi(argv[2]); |
| 102 | printf("v %#x bit %d best %d\n", mask, bit, one_among(mask, bit)); |
| 103 | } |
| 104 | return 0; |
| 105 | } |