Hasher and equality comparison for IOBuf
authorTudor Bosman <tudorb@fb.com>
Mon, 2 Jun 2014 19:08:05 +0000 (12:08 -0700)
committerAnton Likhtarov <alikhtarov@fb.com>
Mon, 9 Jun 2014 22:35:50 +0000 (15:35 -0700)
Test Plan: test added

Reviewed By: davejwatson@fb.com

Subscribers: folly@lists

FB internal diff: D1359469

folly/io/IOBuf.cpp
folly/io/IOBuf.h
folly/io/test/IOBufTest.cpp

index 28a41be5c92975f50e6f7aa203cb626b40b27801..b369eedf1edd1dba8c2bec631c3449ae77ec1788 100644 (file)
@@ -23,6 +23,8 @@
 #include "folly/Malloc.h"
 #include "folly/Memory.h"
 #include "folly/ScopeGuard.h"
+#include "folly/SpookyHashV2.h"
+#include "folly/io/Cursor.h"
 
 #include <stdexcept>
 #include <assert.h>
@@ -893,4 +895,43 @@ folly::fbvector<struct iovec> IOBuf::getIov() const {
   return iov;
 }
 
+size_t IOBufHash::operator()(const IOBuf& buf) const {
+  folly::hash::SpookyHashV2 hasher;
+  hasher.Init(0, 0);
+  io::Cursor cursor(&buf);
+  for (;;) {
+    auto p = cursor.peek();
+    if (p.second == 0) {
+      break;
+    }
+    hasher.Update(p.first, p.second);
+    cursor.skip(p.second);
+  }
+  uint64_t h1;
+  uint64_t h2;
+  hasher.Final(&h1, &h2);
+  return h1;
+}
+
+bool IOBufEqual::operator()(const IOBuf& a, const IOBuf& b) const {
+  io::Cursor ca(&a);
+  io::Cursor cb(&b);
+  for (;;) {
+    auto pa = ca.peek();
+    auto pb = cb.peek();
+    if (pa.second == 0 && pb.second == 0) {
+      return true;
+    } else if (pa.second == 0 || pb.second == 0) {
+      return false;
+    }
+    size_t n = std::min(pa.second, pb.second);
+    DCHECK_GT(n, 0);
+    if (memcmp(pa.first, pb.first, n)) {
+      return false;
+    }
+    ca.skip(n);
+    cb.skip(n);
+  }
+}
+
 } // folly
index 402293436ad0526a9117f97abe683983e3b0b808..2895c0d5a9c9bd72954a64aff94bf63a41acc0bf 100644 (file)
@@ -1265,6 +1265,33 @@ class IOBuf {
   }
 };
 
+/**
+ * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2.
+ */
+struct IOBufHash {
+  size_t operator()(const IOBuf& buf) const;
+  size_t operator()(const std::unique_ptr<IOBuf>& buf) const {
+    return buf ? (*this)(*buf) : 0;
+  }
+};
+
+/**
+ * Equality predicate for IOBuf objects. Compares data in the entire chain.
+ */
+struct IOBufEqual {
+  bool operator()(const IOBuf& a, const IOBuf& b) const;
+  bool operator()(const std::unique_ptr<IOBuf>& a,
+                  const std::unique_ptr<IOBuf>& b) const {
+    if (!a && !b) {
+      return true;
+    } else if (!a || !b) {
+      return false;
+    } else {
+      return (*this)(*a, *b);
+    }
+  }
+};
+
 template <class UniquePtr>
 typename std::enable_if<detail::IsUniquePtrToSL<UniquePtr>::value,
                         std::unique_ptr<IOBuf>>::type
index 868289be5229068d732aebe0bc466e4adda1b3e9..0a1a17c6d0a0b255b4c195881331984985002128 100644 (file)
@@ -1006,6 +1006,67 @@ TEST(IOBuf, move) {
   EXPECT_FALSE(outerBuf.isShared());
 }
 
+namespace {
+std::unique_ptr<IOBuf> fromStr(StringPiece sp) {
+  return IOBuf::copyBuffer(ByteRange(sp));
+}
+}  // namespace
+
+TEST(IOBuf, HashAndEqual) {
+  folly::IOBufEqual eq;
+  folly::IOBufHash hash;
+
+  EXPECT_TRUE(eq(nullptr, nullptr));
+  EXPECT_EQ(0, hash(nullptr));
+
+  auto empty = IOBuf::create(0);
+
+  EXPECT_TRUE(eq(*empty, *empty));
+  EXPECT_TRUE(eq(empty, empty));
+
+  EXPECT_FALSE(eq(nullptr, empty));
+  EXPECT_FALSE(eq(empty, nullptr));
+
+  EXPECT_EQ(hash(*empty), hash(empty));
+  EXPECT_NE(0, hash(empty));
+
+  auto a = fromStr("hello");
+
+  EXPECT_TRUE(eq(*a, *a));
+  EXPECT_TRUE(eq(a, a));
+
+  EXPECT_FALSE(eq(nullptr, a));
+  EXPECT_FALSE(eq(a, nullptr));
+
+  EXPECT_EQ(hash(*a), hash(a));
+  EXPECT_NE(0, hash(a));
+
+  auto b = fromStr("hello");
+
+  EXPECT_TRUE(eq(*a, *b));
+  EXPECT_TRUE(eq(a, b));
+
+  EXPECT_EQ(hash(a), hash(b));
+
+  auto c = fromStr("hellow");
+
+  EXPECT_FALSE(eq(a, c));
+  EXPECT_NE(hash(a), hash(c));
+
+  auto d = fromStr("world");
+
+  EXPECT_FALSE(eq(a, d));
+  EXPECT_NE(hash(a), hash(d));
+
+  auto e = fromStr("helloworld");
+  auto f = fromStr("hello");
+  f->prependChain(fromStr("wo"));
+  f->prependChain(fromStr("rld"));
+
+  EXPECT_TRUE(eq(e, f));
+  EXPECT_EQ(hash(e), hash(f));
+}
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   google::ParseCommandLineFlags(&argc, &argv, true);