Add some CursorBase::operator-() implementations
authorPeter Griess <pgriess@fb.com>
Mon, 28 Jan 2013 18:05:16 +0000 (10:05 -0800)
committerJordan DeLong <jdelong@fb.com>
Tue, 19 Mar 2013 00:05:19 +0000 (17:05 -0700)
Summary:
- Add CursorBase::operator-() implementations for Cursor and BufType;
useful for figuring out the distance between two objects

Test Plan: - Used in some other code

Reviewed By: simpkins@fb.com

FB internal diff: D690046

folly/io/Cursor.h
folly/io/test/IOBufCursorTest.cpp

index 811c2e6ab31e2c1e56513b543be08136dc616c7d..73a5493a7a612526b3e7b22d695f927d87e8a77a 100644 (file)
@@ -216,6 +216,57 @@ class CursorBase {
     }
   }
 
+  /**
+   * Return the distance between two cursors.
+   */
+  size_t operator-(const CursorBase& other) const {
+    BufType *otherBuf = other.crtBuf_;
+    size_t len = 0;
+
+    if (otherBuf != crtBuf_) {
+      len += otherBuf->length() - other.offset_;
+
+      for (otherBuf = otherBuf->next();
+           otherBuf != crtBuf_ && otherBuf != other.buffer_;
+           otherBuf = otherBuf->next()) {
+        len += otherBuf->length();
+      }
+
+      if (otherBuf == other.buffer_) {
+        throw std::out_of_range("wrap-around");
+      }
+
+      len += offset_;
+    } else {
+      if (offset_ < other.offset_) {
+        throw std::out_of_range("underflow");
+      }
+
+      len += offset_ - other.offset_;
+    }
+
+    return len;
+  }
+
+  /**
+   * Return the distance from the given IOBuf to the this cursor.
+   */
+  size_t operator-(const BufType* buf) const {
+    size_t len = 0;
+
+    BufType *curBuf = buf;
+    while (curBuf != crtBuf_) {
+      len += curBuf->length();
+      curBuf = curBuf->next();
+      if (curBuf == buf || curBuf == buffer_) {
+        throw std::out_of_range("wrap-around");
+      }
+    }
+
+    len += offset_;
+    return len;
+  }
+
  protected:
   BufType* crtBuf_;
   size_t offset_;
index c1fe8967c71423e68de6710c1b9e3477e68f3233..3ce32ad6869a3ad2976715143d3d2da1f5f0385f 100644 (file)
@@ -326,6 +326,63 @@ TEST(IOBuf, Appender) {
   EXPECT_EQ("hello world", toString(*head));
 }
 
+TEST(IOBuf, CursorOperators) {
+  // Test operators on a single-item chain
+  {
+    std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
+    chain1->append(10);
+
+    Cursor curs1(chain1.get());
+    EXPECT_EQ(0, curs1 - chain1.get());
+    curs1.skip(3);
+    EXPECT_EQ(3, curs1 - chain1.get());
+    curs1.skip(7);
+    EXPECT_EQ(10, curs1 - chain1.get());
+
+    Cursor curs2(chain1.get());
+    EXPECT_EQ(0, curs2 - chain1.get());
+    EXPECT_EQ(10, curs1 - curs2);
+    EXPECT_THROW(curs2 - curs1, std::out_of_range);
+  }
+
+  // Test cross-chain operations
+  {
+    std::unique_ptr<IOBuf> chain1(IOBuf::create(20));
+    chain1->append(10);
+    std::unique_ptr<IOBuf> chain2 = chain1->clone();
+
+    Cursor curs1(chain1.get());
+    Cursor curs2(chain2.get());
+    EXPECT_THROW(curs1 - curs2, std::out_of_range);
+    EXPECT_THROW(curs1 - chain2.get(), std::out_of_range);
+  }
+
+  // Test operations on multi-item chains
+  {
+    std::unique_ptr<IOBuf> chain(IOBuf::create(20));
+    chain->append(10);
+    chain->appendChain(chain->clone());
+    EXPECT_EQ(20, chain->computeChainDataLength());
+
+    Cursor curs1(chain.get());
+    curs1.skip(5);
+    Cursor curs2(chain.get());
+    curs2.skip(3);
+    EXPECT_EQ(2, curs1 - curs2);
+    EXPECT_EQ(5, curs1 - chain.get());
+    EXPECT_THROW(curs2 - curs1, std::out_of_range);
+
+    curs1.skip(7);
+    EXPECT_EQ(9, curs1 - curs2);
+    EXPECT_EQ(12, curs1 - chain.get());
+    EXPECT_THROW(curs2 - curs1, std::out_of_range);
+
+    curs2.skip(7);
+    EXPECT_EQ(2, curs1 - curs2);
+    EXPECT_THROW(curs2 - curs1, std::out_of_range);
+  }
+}
+
 int benchmark_size = 1000;
 unique_ptr<IOBuf> iobuf_benchmark;