From 0c23a5624a80903fba7e635e8c44f38a79caf223 Mon Sep 17 00:00:00 2001
From: Sage Weil <sage@inktank.com>
Date: Mon, 9 Sep 2013 18:18:26 -0700
Subject: [PATCH] common/buffer: cache crcs in buffer::raw

Signed-off-by: Samuel Just <sam.just@inktank.com>
---
 src/common/buffer.cc   | 69 +++++++++++++++++++++++++++++++++++++++---
 src/test/bufferlist.cc | 17 +++++++++++
 2 files changed, 81 insertions(+), 5 deletions(-)

diff --git a/src/common/buffer.cc b/src/common/buffer.cc
index 8da4c106d1b..09dac01db17 100644
--- a/src/common/buffer.cc
+++ b/src/common/buffer.cc
@@ -21,6 +21,7 @@
 #include "include/atomic.h"
 #include "include/types.h"
 #include "include/compat.h"
+#include "include/Spinlock.h"
 
 #include <errno.h>
 #include <fstream>
@@ -30,6 +31,9 @@
 
 namespace ceph {
 
+
+static unsigned char zbuf[128];
+
 #ifdef BUFFER_DEBUG
 static uint32_t simple_spinlock_t buffer_debug_lock = SIMPLE_SPINLOCK_INITIALIZER;
 # define bdout { simple_spin_lock(&buffer_debug_lock); std::cout
@@ -60,9 +64,14 @@ bool buffer_track_alloc = get_env_bool("CEPH_BUFFER_TRACK");
     unsigned len;
     atomic_t nref;
 
-    raw(unsigned l) : data(NULL), len(l), nref(0)
+    Spinlock crc_lock;
+    map<pair<off_t, off_t>, pair<int64_t, int64_t> > crc_map;
+    int64_t crc_in;   ///< cached crc base; -1 if invalid
+    int64_t crc_out;  ///< cached crc value; -1 if invalid
+
+    raw(unsigned l) : data(NULL), len(l), nref(0), crc_in(-1), crc_out(-1)
     { }
-    raw(char *c, unsigned l) : data(c), len(l), nref(0)
+    raw(char *c, unsigned l) : data(c), len(l), nref(0), crc_in(-1), crc_out(-1)
     { }
     virtual ~raw() {};
 
@@ -77,12 +86,31 @@ bool buffer_track_alloc = get_env_bool("CEPH_BUFFER_TRACK");
       return c;
     }
 
+    unsigned length() const {
+      return len;
+    }
+
     bool is_page_aligned() {
       return ((long)data & ~CEPH_PAGE_MASK) == 0;
     }
     bool is_n_page_sized() {
       return (len & ~CEPH_PAGE_MASK) == 0;
     }
+    bool get_crc(const pair<off_t, off_t> &fromto,
+		 pair<int64_t, int64_t> *crc) const {
+      Spinlock::Locker l(crc_lock);
+      map<pair<off_t, off_t>, pair<int64_t, int64_t> >::const_iterator i =
+	crc_map.find(fromto);
+      if (i == crc_map.end())
+	return false;
+      *crc = i->second;
+      return true;
+    }
+    void set_crc(const pair<off_t, off_t> &fromto,
+		 const pair<uint64_t, uint64_t> &crc) {
+      Spinlock::Locker l(crc_lock);
+      crc_map[fromto] = crc;
+    }
   };
 
   class buffer::raw_malloc : public buffer::raw {
@@ -1274,9 +1302,40 @@ __u32 buffer::list::crc32c(__u32 crc) const
 {
   for (std::list<ptr>::const_iterator it = _buffers.begin();
        it != _buffers.end();
-       ++it)
-    if (it->length())
-      crc = ceph_crc32c(crc, (unsigned char*)it->c_str(), it->length());
+       ++it) {
+    if (it->length()) {
+      raw *r = it->get_raw();
+      pair<off_t, off_t> ofs(it->offset(), it->offset() + it->length());
+      pair<int64_t, int64_t> ccrc;
+      if (r->get_crc(ofs, &ccrc)) {
+	if (ccrc.first == crc) {
+	  // got it already
+	  crc = ccrc.second;
+	} else {
+	  /* If we have cached crc32c(buf, v) for initial value v,
+	   * we can convert this to a different initial value v' by:
+	   * crc32c(buf, v') = crc32c(buf, v) ^ adjustment
+	   * where adjustment = crc32c(0*len(buf), v ^ v')
+	   *
+	   * http://crcutil.googlecode.com/files/crc-doc.1.0.pdf
+	   * note, u for our crc32c implementation is 0
+	   */
+	  int64_t adjustment = ccrc.first ^ crc;
+	  size_t remaining = it->length();
+	  for (; remaining > sizeof(zbuf); remaining -= sizeof(zbuf)) {
+	    adjustment = ceph_crc32c(adjustment, zbuf, sizeof(zbuf));
+	  }
+	  if (remaining)
+	    adjustment = ceph_crc32c(adjustment, zbuf, remaining);
+	  crc = ccrc.second ^ adjustment;
+	}
+      } else {
+	uint32_t base = crc;
+	crc = ceph_crc32c(crc, (unsigned char*)it->c_str(), it->length());
+	r->set_crc(ofs, make_pair(base, crc));
+      }
+    }
+  }
   return crc;
 }
 
diff --git a/src/test/bufferlist.cc b/src/test/bufferlist.cc
index b23bd33e55a..beae868c0da 100644
--- a/src/test/bufferlist.cc
+++ b/src/test/bufferlist.cc
@@ -1649,6 +1649,23 @@ TEST(BufferList, crc32c) {
   EXPECT_EQ((unsigned)0x5FA5C0CC, crc);
 }
 
+TEST(BufferList, crc32cappend) {
+  bufferlist bl1;
+  bufferlist bl2;
+
+  for (int j = 0; j < 200; ++j) {
+    bufferlist bl;
+    for (int i = 0; i < 200; ++i) {
+      char x = rand();
+      bl.append(x);
+      bl1.append(x);
+    }
+    bl.crc32c(rand()); // mess with the cached bufferptr crc values
+    bl2.append(bl);
+  }
+  ASSERT_EQ(bl1.crc32c(0), bl2.crc32c(0));
+}
+
 TEST(BufferList, compare) {
   bufferlist a;
   a.append("A");