add Cursor::isAtEnd()
authorAdam Simpkins <simpkins@fb.com>
Thu, 12 Mar 2015 23:00:31 +0000 (16:00 -0700)
committerAndre Azevedo <aap@fb.com>
Wed, 18 Mar 2015 03:18:45 +0000 (20:18 -0700)
Summary:
Add a helper method to efficiently check if the cursor is at the end of the
IOBuf chain.  This is equivalent to (cursor.totalLength() > 0), but it doesn't
need to walk the entire chain just to tell if it is at the end or not.

Test Plan: Updated the unit tests to contain some checks for isAtEnd().

Reviewed By: jasmeetbagga@fb.com

Subscribers: trunkagent, doug, net-systems@, exa, folly-diffs@, yfeldblum

FB internal diff: D1875345

Signature: t1:1875345:1425006512:49ac246fd0ac7937fdcd6cf1359a841f048c444e

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

index 8bb11bdc5862587c5d6602cdd8a1445cc79632b9..2aa4227f3386ded5e53dffee3e70883de51f563b 100644 (file)
@@ -108,6 +108,30 @@ class CursorBase {
     return end - *this;
   }
 
+  /*
+   * Return true if the cursor is at the end of the entire IOBuf chain.
+   */
+  bool isAtEnd() const {
+    // Check for the simple cases first.
+    if (offset_ != crtBuf_->length()) {
+      return false;
+    }
+    if (crtBuf_ == buffer_->prev()) {
+      return true;
+    }
+    // We are at the end of a buffer, but it isn't the last buffer.
+    // We might still be at the end if the remaining buffers in the chain are
+    // empty.
+    const IOBuf* buf = crtBuf_->next();;
+    while (buf != buffer_) {
+      if (buf->length() > 0) {
+        return false;
+      }
+      buf = buf->next();
+    }
+    return true;
+  }
+
   Derived& operator+=(size_t offset) {
     Derived* p = static_cast<Derived*>(this);
     p->skip(offset);
index 9cd061d53c0c7a6d1a4551bc9223de08481b96e6..3d1a4014e339f8ab7e5252162143aded979543e5 100644 (file)
@@ -263,9 +263,11 @@ TEST(IOBuf, pushCursorData) {
 
   //write 20 bytes to the buffer chain
   RWPrivateCursor wcursor(iobuf1.get());
+  EXPECT_FALSE(wcursor.isAtEnd());
   wcursor.writeBE<uint64_t>(1);
   wcursor.writeBE<uint64_t>(10);
   wcursor.writeBE<uint32_t>(20);
+  EXPECT_TRUE(wcursor.isAtEnd());
 
   // create a read buffer for the buffer chain
   Cursor rcursor(iobuf1.get());
@@ -320,6 +322,7 @@ TEST(IOBuf, Gather) {
   cursor.gatherAtMost(10);
   EXPECT_EQ(8, cursor.length());
   EXPECT_EQ(8, cursor.totalLength());
+  EXPECT_FALSE(cursor.isAtEnd());
   EXPECT_EQ("lo world",
             folly::StringPiece(reinterpret_cast<const char*>(cursor.data()),
                                cursor.length()));
@@ -504,10 +507,13 @@ TEST(IOBuf, CursorOperators) {
 
     Cursor curs1(chain1.get());
     EXPECT_EQ(0, curs1 - chain1.get());
+    EXPECT_FALSE(curs1.isAtEnd());
     curs1.skip(3);
     EXPECT_EQ(3, curs1 - chain1.get());
+    EXPECT_FALSE(curs1.isAtEnd());
     curs1.skip(7);
     EXPECT_EQ(10, curs1 - chain1.get());
+    EXPECT_TRUE(curs1.isAtEnd());
 
     Cursor curs2(chain1.get());
     EXPECT_EQ(0, curs2 - chain1.get());
@@ -551,6 +557,26 @@ TEST(IOBuf, CursorOperators) {
     EXPECT_EQ(2, curs1 - curs2);
     EXPECT_THROW(curs2 - curs1, std::out_of_range);
   }
+
+  // Test isAtEnd() with empty buffers at the end of a chain
+  {
+    auto iobuf1 = IOBuf::create(20);
+    iobuf1->append(15);
+    iobuf1->trimStart(5);
+
+    Cursor c(iobuf1.get());
+    EXPECT_FALSE(c.isAtEnd());
+    c.skip(10);
+    EXPECT_TRUE(c.isAtEnd());
+
+    iobuf1->prependChain(IOBuf::create(10));
+    iobuf1->prependChain(IOBuf::create(10));
+    EXPECT_TRUE(c.isAtEnd());
+    iobuf1->prev()->append(5);
+    EXPECT_FALSE(c.isAtEnd());
+    c.skip(5);
+    EXPECT_TRUE(c.isAtEnd());
+  }
 }
 
 TEST(IOBuf, StringOperations) {