Add fnv1-a hash to Hash
authorSubodh Iyengar <subodh@fb.com>
Sun, 23 Jul 2017 18:15:06 +0000 (11:15 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Sun, 23 Jul 2017 18:24:16 +0000 (11:24 -0700)
Summary:
Add fnv1-a 64 bit hash to Hash.h.
fnv1-a is supposed to be preferred over
fnv1.

Reviewed By: yfeldblum

Differential Revision: D5453401

fbshipit-source-id: c13f12ef11eb6745c95f3901b14ceafce90ea0ea

folly/Hash.h
folly/test/HashTest.cpp

index 77ba5878f17e6502c0c89a0e2fbe42f8c4dc757f..90492180f6a41f10ce37227d394add19b8d5ea28 100644 (file)
@@ -214,6 +214,7 @@ inline uint32_t jenkins_rev_unmix32(uint32_t key) {
 
 const uint32_t FNV_32_HASH_START = 2166136261UL;
 const uint64_t FNV_64_HASH_START = 14695981039346656037ULL;
+const uint64_t FNVA_64_HASH_START = 14695981039346656037ULL;
 
 inline uint32_t fnv32(const char* buf, uint32_t hash = FNV_32_HASH_START) {
   // forcing signed char, since other platforms can use unsigned
@@ -278,6 +279,24 @@ inline uint64_t fnv64(const std::string& str,
   return fnv64_buf(str.data(), str.size(), hash);
 }
 
+inline uint64_t fnva64_buf(const void* buf,
+                           size_t n,
+                           uint64_t hash = FNVA_64_HASH_START) {
+  const uint8_t* char_buf = reinterpret_cast<const uint8_t*>(buf);
+
+  for (size_t i = 0; i < n; ++i) {
+    hash ^= char_buf[i];
+    hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) +
+            (hash << 8) + (hash << 40);
+  }
+  return hash;
+}
+
+inline uint64_t fnva64(const std::string& str,
+                       uint64_t hash = FNVA_64_HASH_START) {
+  return fnva64_buf(str.data(), str.size(), hash);
+}
+
 /*
  * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
  */
index 01cbf2e39e96f917731706f80b1b212bf5a8b1ea..070cf78f68ab27967cca065a7ff414bd776eca8c 100644 (file)
@@ -376,3 +376,45 @@ TEST(Hash, Strings) {
   EXPECT_EQ(h2(a3), h2(a3.str()));
   EXPECT_EQ(h2(a4), h2(a4.str()));
 }
+
+struct FNVTestParam {
+  std::string in;
+  uint64_t out;
+};
+
+class FNVTest : public ::testing::TestWithParam<FNVTestParam> {};
+
+TEST_P(FNVTest, Fnva64Buf) {
+  EXPECT_EQ(GetParam().out,
+            fnva64_buf(GetParam().in.data(), GetParam().in.size()));
+}
+
+TEST_P(FNVTest, Fnva64) {
+  EXPECT_EQ(GetParam().out, fnva64(GetParam().in));
+}
+
+TEST_P(FNVTest, Fnva64Partial) {
+  size_t partialLen = GetParam().in.size() / 2;
+  auto data = GetParam().in.data();
+  auto partial = fnva64_buf(data, partialLen);
+  EXPECT_EQ(GetParam().out,
+            fnva64_buf(
+                data + partialLen, GetParam().in.size() - partialLen, partial));
+}
+
+// Taken from http://www.isthe.com/chongo/src/fnv/test_fnv.c
+INSTANTIATE_TEST_CASE_P(
+    FNVTesting,
+    FNVTest,
+    ::testing::Values(
+        (FNVTestParam){"foobar", // 11
+                       0x85944171f73967e8},
+        (FNVTestParam){"chongo was here!\n", // 39
+                       0x46810940eff5f915},
+        (FNVTestParam){"127.0.0.3", // 106,
+                       0xaabafc7104d91158},
+        (FNVTestParam){
+            "http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash", // 126
+            0xd9b957fb7fe794c5},
+        (FNVTestParam){"http://norvig.com/21-days.html", // 136
+                       0x07aaa640476e0b9a}));