diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h index f84f02d7d..7b876f49b 100644 --- a/include/haproxy/tools.h +++ b/include/haproxy/tools.h @@ -1324,6 +1324,50 @@ static inline uint statistical_prng_range(uint range) return mul32hi(statistical_prng(), range ? range - 1 : 0); } +/* The functions below are used to hash one or two pointers together and reduce + * the result to fit into a given number of bits. The first part is made of a + * multiplication (and possibly an addition) by one or two prime numbers giving + * a 64-bit number whose center bits are the most distributed, and the second + * part will reuse this value and return a mix of the most variable bits that + * fits in the requested size. The most convenient approach is to directly + * call ptr_hash() / ptr2_hash(), though for some specific use cases where a + * second value could be useful, one may prefer to call the lower level + * operations instead. + */ + +/* reduce a 64-bit pointer hash to bits */ +static forceinline uint _ptr_hash_reduce(unsigned long long x, const int bits) +{ + if (!bits) + return 0; + + if (sizeof(long) == 4) + x ^= x >> 32; + else + x >>= 31 - (bits + 1) / 2; + return x & (~0U >> (-bits & 31)); +} + +/* single-pointer version, low-level, use ptr_hash() instead */ +static forceinline ullong _ptr_hash(const void *p) +{ + unsigned long long x = (unsigned long)p; + + x *= 0xacd1be85U; + return x; +} + +/* two-pointer version, low-level, use ptr2_hash() instead */ +static forceinline ullong _ptr2_hash(const void *p1, const void *p2) +{ + unsigned long long x = (unsigned long)p1; + unsigned long long y = (unsigned long)p2; + + x *= 0xacd1be85U; + y *= 0x9d28e4e9U; + return x ^ y; +} + /* returns a hash on bits of pointer

that is suitable for being used * to compute statistic buckets, in that it's fast and reasonably distributed * thanks to mixing the bits via a multiplication by a prime number and using @@ -1337,17 +1381,7 @@ static inline uint statistical_prng_range(uint range) */ static forceinline uint ptr_hash(const void *p, const int bits) { - unsigned long long x = (unsigned long)p; - - if (!bits) - return 0; - - x *= 0xacd1be85U; - if (sizeof(long) == 4) - x ^= x >> 32; - else - x >>= 31 - (bits + 1) / 2; - return x & (~0U >> (-bits & 31)); + return _ptr_hash_reduce(_ptr_hash(p), bits); } /* Same as above but works on two pointers. It will return the same values @@ -1355,20 +1389,7 @@ static forceinline uint ptr_hash(const void *p, const int bits) */ static forceinline uint ptr2_hash(const void *p1, const void *p2, const int bits) { - unsigned long long x = (unsigned long)p1; - unsigned long long y = (unsigned long)p2; - - if (!bits) - return 0; - - x *= 0xacd1be85U; - y *= 0x9d28e4e9U; - x ^= y; - if (sizeof(long) == 4) - x ^= x >> 32; - else - x >>= 33 - bits / 2; - return x & (~0U >> (-bits & 31)); + return _ptr_hash_reduce(_ptr2_hash(p1, p2), bits); }