Add canAdvance to cursor and tests
authorNeel Goyal <ngoyal@fb.com>
Tue, 5 Jan 2016 17:33:55 +0000 (09:33 -0800)
committerfacebook-github-bot-0 <folly-bot@fb.com>
Tue, 5 Jan 2016 18:20:23 +0000 (10:20 -0800)
Summary:
Determine if the cursor can advance N bytes. This is useful if
applications want to check before reading so an exception isn't thrown.
It tries to walk the minimal amount of links needed in the chain.

I had a task that could have used this, though caching totalLength and
macro magic ended up being the implementation chosen. I think this just
adds to the cursor API.

Reviewed By: djwatson

Differential Revision: D2728498

fb-gh-sync-id: 8657653b82a48828cccab143653dc169ef715702

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

index d38d4dfbc472dd6148cb5c6e3cf57a1fd6a53e21..477f8ec7839411802dd963abb26019de9670b5e2 100644 (file)
@@ -108,6 +108,27 @@ class CursorBase {
     return end - *this;
   }
 
+  /**
+   * Return true if the cursor could advance the specified number of bytes
+   * from its current position.
+   * This is useful for applications that want to do checked reads instead of
+   * catching exceptions and is more efficient than using totalLength as it
+   * walks the minimal set of buffers in the chain to determine the result.
+   */
+  bool canAdvance(size_t amount) const {
+    const IOBuf* nextBuf = crtBuf_;
+    size_t available = length();
+    do {
+      if (available >= amount) {
+        return true;
+      }
+      amount -= available;
+      nextBuf = nextBuf->next();
+      available = nextBuf->length();
+    } while (nextBuf != buffer_);
+    return false;
+  }
+
   /*
    * Return true if the cursor is at the end of the entire IOBuf chain.
    */
index 31801b8a14c25947e535147448f9ad5fb47eb55a..459b222afa5eaa4b871ebd78965ea885aab133ea 100644 (file)
@@ -618,6 +618,24 @@ TEST(IOBuf, CursorOperators) {
     c.skip(5);
     EXPECT_TRUE(c.isAtEnd());
   }
+
+  // Test canAdvance with a chain of items
+  {
+    auto chain = IOBuf::create(10);
+    chain->append(10);
+    chain->appendChain(chain->clone());
+    EXPECT_EQ(2, chain->countChainElements());
+    EXPECT_EQ(20, chain->computeChainDataLength());
+
+    Cursor c(chain.get());
+    for (size_t i = 0; i <= 20; ++i) {
+      EXPECT_TRUE(c.canAdvance(i));
+    }
+    EXPECT_FALSE(c.canAdvance(21));
+    c.skip(10);
+    EXPECT_TRUE(c.canAdvance(10));
+    EXPECT_FALSE(c.canAdvance(11));
+  }
 }
 
 TEST(IOBuf, StringOperations) {