diff --git a/folly/Hash.h b/folly/Hash.h index a6e330465a9..efe0f047194 100644 --- a/folly/Hash.h +++ b/folly/Hash.h @@ -69,13 +69,32 @@ size_t hash_combine(const T& t, const Ts&... ts) { */ inline uint64_t twang_mix64(uint64_t key) { - key = (~key) + (key << 21); + key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1; key = key ^ (key >> 24); - key = (key + (key << 3)) + (key << 8); + key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8) key = key ^ (key >> 14); - key = (key + (key << 2)) + (key << 4); + key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4) key = key ^ (key >> 28); - key = key + (key << 31); + key = key + (key << 31); // key *= 1 + (1 << 31) + return key; +} + +/* + * Inverse of twang_mix64 + * + * Note that twang_unmix64 is significantly slower than twang_mix64. + */ + +inline uint64_t twang_unmix64(uint64_t key) { + // See the comments in jenkins_rev_unmix32 for an explanation as to how this + // was generated + key *= 4611686016279904257U; + key ^= (key >> 28) ^ (key >> 56); + key *= 14933078535860113213U; + key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56); + key *= 15244667743933553977U; + key ^= (key >> 24) ^ (key >> 48); + key = (key + 1) * 9223367638806167551U; return key; } @@ -98,17 +117,50 @@ inline uint32_t twang_32from64(uint64_t key) { */ inline uint32_t jenkins_rev_mix32(uint32_t key) { - key += (key << 12); + key += (key << 12); // key *= (1 + (1 << 12)) key ^= (key >> 22); - key += (key << 4); + key += (key << 4); // key *= (1 + (1 << 4)) key ^= (key >> 9); - key += (key << 10); + key += (key << 10); // key *= (1 + (1 << 10)) key ^= (key >> 2); + // key *= (1 + (1 << 7)) * (1 + (1 << 12)) key += (key << 7); key += (key << 12); return key; } +/* + * Inverse of jenkins_rev_mix32 + * + * Note that jenkinks_rev_unmix32 is significantly slower than + * jenkins_rev_mix32. + */ + +inline uint32_t jenkins_rev_unmix32(uint32_t key) { + // These are the modular multiplicative inverses (in Z_2^32) of the + // multiplication factors in jenkins_rev_mix32, in reverse order. They were + // computed using the Extended Euclidean algorithm, see + // http://en.wikipedia.org/wiki/Modular_multiplicative_inverse + key *= 2364026753U; + + // The inverse of a ^= (a >> n) is + // b = a + // for (int i = n; i < 32; i += n) { + // b ^= (a >> i); + // } + key ^= + (key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^ + (key >> 10) ^ (key >> 12) ^ (key >> 14) ^ (key >> 16) ^ + (key >> 18) ^ (key >> 20) ^ (key >> 22) ^ (key >> 24) ^ + (key >> 26) ^ (key >> 28) ^ (key >> 30); + key *= 3222273025U; + key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27); + key *= 4042322161U; + key ^= (key >> 22); + key *= 16773121U; + return key; +} + /* * Fowler / Noll / Vo (FNV) Hash * http://www.isthe.com/chongo/tech/comp/fnv/ diff --git a/folly/test/HashTest.cpp b/folly/test/HashTest.cpp index f06f8b84495..954b6b6d299 100644 --- a/folly/test/HashTest.cpp +++ b/folly/test/HashTest.cpp @@ -102,11 +102,29 @@ TEST(Hash, Hsieh32) { TEST(Hash, TWang_Mix64) { uint64_t i1 = 0x78a87873e2d31dafULL; uint64_t i1_res = 3389151152926383528ULL; - EXPECT_EQ(twang_mix64(i1), i1_res); + EXPECT_EQ(i1_res, twang_mix64(i1)); + EXPECT_EQ(i1, twang_unmix64(i1_res)); uint64_t i2 = 0x0123456789abcdefULL; uint64_t i2_res = 3061460455458984563ull; - EXPECT_EQ(twang_mix64(i2), i2_res); + EXPECT_EQ(i2_res, twang_mix64(i2)); + EXPECT_EQ(i2, twang_unmix64(i2_res)); +} + +namespace { +void checkTWang(uint64_t r) { + uint64_t result = twang_mix64(r); + EXPECT_EQ(r, twang_unmix64(result)); +} +} // namespace + +TEST(Hash, TWang_Unmix64) { + // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1 + for (int i = 1; i < 64; i++) { + checkTWang((1U << i) - 1); + checkTWang(1U << i); + checkTWang((1U << i) + 1); + } } TEST(Hash, TWang_32From64) { @@ -122,11 +140,29 @@ TEST(Hash, TWang_32From64) { TEST(Hash, Jenkins_Rev_Mix32) { uint32_t i1 = 3805486511ul; uint32_t i1_res = 381808021ul; - EXPECT_EQ(jenkins_rev_mix32(i1), i1_res); + EXPECT_EQ(i1_res, jenkins_rev_mix32(i1)); + EXPECT_EQ(i1, jenkins_rev_unmix32(i1_res)); uint32_t i2 = 2309737967ul; uint32_t i2_res = 1834777923ul; - EXPECT_EQ(jenkins_rev_mix32(i2), i2_res); + EXPECT_EQ(i2_res, jenkins_rev_mix32(i2)); + EXPECT_EQ(i2, jenkins_rev_unmix32(i2_res)); +} + +namespace { +void checkJenkins(uint32_t r) { + uint32_t result = jenkins_rev_mix32(r); + EXPECT_EQ(r, jenkins_rev_unmix32(result)); +} +} // namespace + +TEST(Hash, Jenkins_Rev_Unmix32) { + // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1 + for (int i = 1; i < 32; i++) { + checkJenkins((1U << i) - 1); + checkJenkins(1U << i); + checkJenkins((1U << i) + 1); + } } TEST(Hash, hasher) {