/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/io/IOBuf.h>
-#include <gflags/gflags.h>
-#include <boost/random.hpp>
-#include <gtest/gtest.h>
-#include <folly/Benchmark.h>
+#include <folly/Format.h>
#include <folly/Range.h>
#include <folly/io/Cursor.h>
+#include <folly/portability/GTest.h>
-DECLARE_bool(benchmark);
-
+using folly::ByteRange;
+using folly::format;
using folly::IOBuf;
+using folly::StringPiece;
using std::unique_ptr;
using namespace folly::io;
unique_ptr<IOBuf> iobuf2(IOBuf::create(20));
iobuf2->append(20);
- IOBuf* iob2ptr = iobuf2.get();
+ iobuf2.get();
iobuf1->prependChain(std::move(iobuf2));
EXPECT_TRUE(iobuf1->isChained());
wcursor.writeLE((uint64_t)1);
wcursor.write((uint8_t)1);
- EXPECT_EQ(1, rcursor.readLE<uint64_t>());
+ EXPECT_EQ(1u, rcursor.readLE<uint64_t>());
rcursor.skip(8);
- EXPECT_EQ(1, rcursor.readLE<uint32_t>());
+ EXPECT_EQ(1u, rcursor.readLE<uint32_t>());
rcursor.skip(0);
- EXPECT_EQ(0, rcursor.read<uint8_t>());
- EXPECT_EQ(0, rcursor.read<uint8_t>());
- EXPECT_EQ(0, rcursor.read<uint8_t>());
- EXPECT_EQ(0, rcursor.read<uint8_t>());
- EXPECT_EQ(1, rcursor.read<uint8_t>());
+ EXPECT_EQ(0u, rcursor.read<uint8_t>());
+ EXPECT_EQ(0u, rcursor.read<uint8_t>());
+ EXPECT_EQ(0u, rcursor.read<uint8_t>());
+ EXPECT_EQ(0u, rcursor.read<uint8_t>());
+ EXPECT_EQ(1u, rcursor.read<uint8_t>());
}
TEST(IOBuf, skip) {
buf->append(data.size());
}
-void append(Appender& appender, folly::StringPiece data) {
- appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
+void append(Appender& appender, StringPiece data) {
+ appender.push(ByteRange(data));
}
std::string toString(const IOBuf& buf) {
std::string str;
Cursor cursor(&buf);
- std::pair<const uint8_t*, size_t> p;
- while ((p = cursor.peek()).second) {
- str.append(reinterpret_cast<const char*>(p.first), p.second);
- cursor.skip(p.second);
+ ByteRange b;
+ while (!(b = cursor.peekBytes()).empty()) {
+ str.append(reinterpret_cast<const char*>(b.data()), b.size());
+ cursor.skip(b.size());
}
return str;
}
{
RWPrivateCursor cursor(iobuf1.get());
- auto p = cursor.peek();
- EXPECT_EQ("he", std::string(reinterpret_cast<const char*>(p.first),
- p.second));
- cursor.skip(p.second);
- p = cursor.peek();
- EXPECT_EQ("llo ", std::string(reinterpret_cast<const char*>(p.first),
- p.second));
- cursor.skip(p.second);
- p = cursor.peek();
- EXPECT_EQ("world", std::string(reinterpret_cast<const char*>(p.first),
- p.second));
- cursor.skip(p.second);
+ auto b = cursor.peekBytes();
+ EXPECT_EQ("he", StringPiece(b));
+ cursor.skip(b.size());
+ b = cursor.peekBytes();
+ EXPECT_EQ("llo ", StringPiece(b));
+ cursor.skip(b.size());
+ b = cursor.peekBytes();
+ EXPECT_EQ("world", StringPiece(b));
+ cursor.skip(b.size());
EXPECT_EQ(3, iobuf1->countChainElements());
EXPECT_EQ(11, iobuf1->computeChainDataLength());
}
{
RWPrivateCursor cursor(iobuf1.get());
cursor.gather(11);
- auto p = cursor.peek();
- EXPECT_EQ("hello world", std::string(reinterpret_cast<const
- char*>(p.first), p.second));
+ auto b = cursor.peekBytes();
+ EXPECT_EQ("hello world", StringPiece(b));
EXPECT_EQ(1, iobuf1->countChainElements());
EXPECT_EQ(11, iobuf1->computeChainDataLength());
}
}
+TEST(IOBuf, pushCursorData) {
+ unique_ptr<IOBuf> iobuf1(IOBuf::create(20));
+ iobuf1->append(15);
+ iobuf1->trimStart(5);
+ unique_ptr<IOBuf> iobuf2(IOBuf::create(10));
+ unique_ptr<IOBuf> iobuf3(IOBuf::create(10));
+ iobuf3->append(10);
+
+ iobuf1->prependChain(std::move(iobuf2));
+ iobuf1->prependChain(std::move(iobuf3));
+ EXPECT_TRUE(iobuf1->isChained());
+
+ //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());
+ EXPECT_EQ(1, rcursor.readBE<uint64_t>());
+ EXPECT_EQ(10, rcursor.readBE<uint64_t>());
+ EXPECT_EQ(20, rcursor.readBE<uint32_t>());
+ EXPECT_EQ(0, rcursor.totalLength());
+ rcursor.reset(iobuf1.get());
+ EXPECT_EQ(20, rcursor.totalLength());
+
+ // create another write buffer
+ unique_ptr<IOBuf> iobuf4(IOBuf::create(30));
+ iobuf4->append(30);
+ RWPrivateCursor wcursor2(iobuf4.get());
+ // write buffer chain data into it, now wcursor2 should only
+ // have 10 bytes writable space
+ wcursor2.push(rcursor, 20);
+ EXPECT_EQ(wcursor2.totalLength(), 10);
+ // write again with not enough space in rcursor
+ EXPECT_THROW(wcursor2.push(rcursor, 20), std::out_of_range);
+
+ // create a read cursor to check iobuf3 data back
+ Cursor rcursor2(iobuf4.get());
+ EXPECT_EQ(1, rcursor2.readBE<uint64_t>());
+ EXPECT_EQ(10, rcursor2.readBE<uint64_t>());
+ EXPECT_EQ(20, rcursor2.readBE<uint32_t>());
+
+}
+
TEST(IOBuf, Gather) {
std::unique_ptr<IOBuf> iobuf1(IOBuf::create(10));
append(iobuf1, "he");
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()));
EXPECT_EQ(7, iobuf1->countChainElements());
EXPECT_EQ(14, iobuf1->computeChainDataLength());
// Check that nextBuf got set correctly to the buffer with 1 byte left
- EXPECT_EQ(1, cursor.peek().second);
+ EXPECT_EQ(1, cursor.peekBytes().size());
cursor.read<uint8_t>();
}
}
}
+TEST(IOBuf, cloneWithEmptyBufAtStart) {
+ folly::IOBufEqual eq;
+ auto empty = IOBuf::create(0);
+ auto hel = IOBuf::create(3);
+ append(hel, "hel");
+ auto lo = IOBuf::create(2);
+ append(lo, "lo");
+
+ auto iobuf = empty->clone();
+ iobuf->prependChain(hel->clone());
+ iobuf->prependChain(lo->clone());
+ iobuf->prependChain(empty->clone());
+ iobuf->prependChain(hel->clone());
+ iobuf->prependChain(lo->clone());
+ iobuf->prependChain(empty->clone());
+ iobuf->prependChain(lo->clone());
+ iobuf->prependChain(hel->clone());
+ iobuf->prependChain(lo->clone());
+ iobuf->prependChain(lo->clone());
+
+ Cursor cursor(iobuf.get());
+ std::unique_ptr<IOBuf> cloned;
+ char data[3];
+ cursor.pull(&data, 3);
+ cursor.clone(cloned, 2);
+ EXPECT_EQ(1, cloned->countChainElements());
+ EXPECT_EQ(2, cloned->length());
+ EXPECT_TRUE(eq(lo, cloned));
+
+ cursor.pull(&data, 3);
+ EXPECT_EQ("hel", std::string(data, sizeof(data)));
+
+ cursor.skip(2);
+ cursor.clone(cloned, 2);
+ EXPECT_TRUE(eq(lo, cloned));
+
+ std::string hello = cursor.readFixedString(5);
+ cursor.clone(cloned, 2);
+ EXPECT_TRUE(eq(lo, cloned));
+}
+
TEST(IOBuf, Appender) {
std::unique_ptr<IOBuf> head(IOBuf::create(10));
append(head, "hello");
Appender app(head.get(), 10);
- uint32_t cap = head->capacity();
- uint32_t len1 = app.length();
+ auto cap = head->capacity();
+ auto len1 = app.length();
EXPECT_EQ(cap - 5, len1);
app.ensure(len1); // won't grow
EXPECT_EQ(len1, app.length());
EXPECT_EQ("hello world", toString(*head));
}
+TEST(IOBuf, Printf) {
+ IOBuf head(IOBuf::CREATE, 24);
+ Appender app(&head, 32);
+
+ app.printf("%s", "test");
+ EXPECT_EQ(head.length(), 4);
+ EXPECT_EQ(0, memcmp(head.data(), "test\0", 5));
+
+ app.printf("%d%s %s%s %#x", 32, "this string is",
+ "longer than our original allocation size,",
+ "and will therefore require a new allocation", 0x12345678);
+ // The tailroom should start with a nul byte now.
+ EXPECT_GE(head.prev()->tailroom(), 1u);
+ EXPECT_EQ(0, *head.prev()->tail());
+
+ EXPECT_EQ("test32this string is longer than our original "
+ "allocation size,and will therefore require a "
+ "new allocation 0x12345678",
+ head.moveToFbString().toStdString());
+}
+
+TEST(IOBuf, Format) {
+ IOBuf head(IOBuf::CREATE, 24);
+ Appender app(&head, 32);
+
+ format("{}", "test")(app);
+ EXPECT_EQ(head.length(), 4);
+ EXPECT_EQ(0, memcmp(head.data(), "test", 4));
+
+ auto fmt = format("{}{} {}{} {:#x}",
+ 32, "this string is",
+ "longer than our original allocation size,",
+ "and will therefore require a new allocation",
+ 0x12345678);
+ fmt(app);
+ EXPECT_EQ("test32this string is longer than our original "
+ "allocation size,and will therefore require a "
+ "new allocation 0x12345678",
+ head.moveToFbString().toStdString());
+}
+
TEST(IOBuf, QueueAppender) {
folly::IOBufQueue queue;
}
// There must be a goodMallocSize between 100 and 1024...
- EXPECT_LT(1, queue.front()->countChainElements());
+ EXPECT_LT(1u, queue.front()->countChainElements());
const IOBuf* buf = queue.front();
do {
- EXPECT_LE(100, buf->capacity());
+ EXPECT_LE(100u, buf->capacity());
buf = buf->next();
} while (buf != queue.front());
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());
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 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) {
EXPECT_STREQ("hello", curs.readTerminatedString().c_str());
}
+ // Test reading a null-terminated string from a chain that doesn't contain the
+ // terminator
+ {
+ std::unique_ptr<IOBuf> buf(IOBuf::create(8));
+ Appender app(buf.get(), 0);
+ app.push(reinterpret_cast<const uint8_t*>("hello"), 5);
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_THROW(curs.readTerminatedString(),
+ std::out_of_range);
+ }
+
+ // Test reading a null-terminated string past the maximum length
+ {
+ std::unique_ptr<IOBuf> buf(IOBuf::create(8));
+ Appender app(buf.get(), 0);
+ app.push(reinterpret_cast<const uint8_t*>("hello\0"), 6);
+ std::unique_ptr<IOBuf> chain(IOBuf::create(8));
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_THROW(curs.readTerminatedString('\0', 3),
+ std::length_error);
+ }
+
// Test reading a two fixed-length strings from a single buffer with an extra
// uint8_t at the end
{
}
}
-int benchmark_size = 1000;
-unique_ptr<IOBuf> iobuf_benchmark;
+TEST(IOBuf, ReadWhileTrue) {
+ auto isAlpha = [](uint8_t ch) {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+ };
+ auto isDigit = [](uint8_t ch) { return (ch >= '0' && ch <= '9'); };
-unique_ptr<IOBuf> iobuf_read_benchmark;
+ // Test reading alternating alphabetic and numeric strings
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(32));
+ Appender app(chain.get(), 0);
+ app.push(StringPiece("hello123world456"));
-template <class CursClass>
-void runBenchmark() {
- CursClass c(iobuf_benchmark.get());
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
+ EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
+ EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
+ EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
+ EXPECT_TRUE(curs.isAtEnd());
+ }
- for(int i = 0; i < benchmark_size; i++) {
- c.write((uint8_t)0);
+ // The same, but also use skipWhile()
+ {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(16));
+ Appender app(chain.get(), 0);
+ app.push(StringPiece("hello123world456"));
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
+ curs.skipWhile(isDigit);
+ curs.skipWhile(isAlpha);
+ EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
+ EXPECT_TRUE(curs.isAtEnd());
}
-}
-BENCHMARK(rwPrivateCursorBenchmark, iters) {
- while (iters--) {
- runBenchmark<RWPrivateCursor>();
+ // Test readWhile() using data split across multiple buffers,
+ // including some empty buffers in the middle of the chain.
+ {
+ std::unique_ptr<IOBuf> chain;
+
+ // First element in the chain has "he"
+ auto buf = IOBuf::create(40);
+ Appender app(buf.get(), 0);
+ app.push(StringPiece("he"));
+ chain = std::move(buf);
+
+ // The second element has "ll", after 10 bytes of headroom
+ buf = IOBuf::create(40);
+ buf->advance(10);
+ app = Appender{buf.get(), 0};
+ app.push(StringPiece("ll"));
+ chain->prependChain(std::move(buf));
+
+ // The third element is empty
+ buf = IOBuf::create(40);
+ buf->advance(15);
+ chain->prependChain(std::move(buf));
+
+ // The fourth element has "o12"
+ buf = IOBuf::create(40);
+ buf->advance(37);
+ app = Appender{buf.get(), 0};
+ app.push(StringPiece("o12"));
+ chain->prependChain(std::move(buf));
+
+ // The fifth element has "3"
+ buf = IOBuf::create(40);
+ app = Appender{buf.get(), 0};
+ app.push(StringPiece("3"));
+ chain->prependChain(std::move(buf));
+
+ // The sixth element is empty
+ buf = IOBuf::create(40);
+ chain->prependChain(std::move(buf));
+
+ // The seventh element has "world456"
+ buf = IOBuf::create(40);
+ app = Appender{buf.get(), 0};
+ app.push(StringPiece("world456"));
+ chain->prependChain(std::move(buf));
+
+ // The eighth element is empty
+ buf = IOBuf::create(40);
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_STREQ("hello", curs.readWhile(isAlpha).c_str());
+ EXPECT_STREQ("123", curs.readWhile(isDigit).c_str());
+ EXPECT_STREQ("world", curs.readWhile(isAlpha).c_str());
+ EXPECT_STREQ("456", curs.readWhile(isDigit).c_str());
+ EXPECT_TRUE(curs.isAtEnd());
}
}
-BENCHMARK(rwUnshareCursorBenchmark, iters) {
- while (iters--) {
- runBenchmark<RWUnshareCursor>();
- }
+TEST(IOBuf, TestAdvanceToEndSingle) {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(10));
+ chain->append(10);
+
+ Cursor curs(chain.get());
+ curs.advanceToEnd();
+ EXPECT_TRUE(curs.isAtEnd());
+ EXPECT_EQ(curs - chain.get(), 10);
}
+TEST(IOBuf, TestAdvanceToEndMulti) {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(10));
+ chain->append(10);
-BENCHMARK(cursorBenchmark, iters) {
- while (iters--) {
- Cursor c(iobuf_read_benchmark.get());
- for(int i = 0; i < benchmark_size ; i++) {
- c.read<uint8_t>();
- }
- }
+ std::unique_ptr<IOBuf> buf(IOBuf::create(5));
+ buf->append(5);
+ chain->prependChain(std::move(buf));
+
+ buf = IOBuf::create(20);
+ buf->append(20);
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ curs.advanceToEnd();
+ EXPECT_TRUE(curs.isAtEnd());
+ EXPECT_EQ(curs - chain.get(), 35);
+
+ curs.reset(chain.get());
+ curs.skip(12);
+ curs.advanceToEnd();
+ EXPECT_TRUE(curs.isAtEnd());
}
-BENCHMARK(skipBenchmark, iters) {
- uint8_t buf;
- while (iters--) {
- Cursor c(iobuf_read_benchmark.get());
- for(int i = 0; i < benchmark_size ; i++) {
- c.peek();
- c.skip(1);
- }
- }
+TEST(IOBuf, TestRetreatSingle) {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(20));
+ chain->append(20);
+
+ Cursor curs(chain.get());
+ EXPECT_EQ(curs.retreatAtMost(0), 0);
+ EXPECT_EQ(curs.totalLength(), 20);
+ EXPECT_EQ(curs.retreatAtMost(5), 0);
+ EXPECT_EQ(curs.totalLength(), 20);
+ EXPECT_EQ(curs.retreatAtMost(25), 0);
+ EXPECT_EQ(curs.totalLength(), 20);
+
+ curs.retreat(0);
+ EXPECT_THROW(curs.retreat(5), std::out_of_range);
+ curs.reset(chain.get());
+ EXPECT_THROW(curs.retreat(25), std::out_of_range);
+ curs.reset(chain.get());
+
+ curs.advanceToEnd();
+ curs.retreat(5);
+ EXPECT_EQ(curs.totalLength(), 5);
+ curs.retreat(10);
+ EXPECT_EQ(curs.totalLength(), 15);
+ EXPECT_THROW(curs.retreat(10), std::out_of_range);
+
+ curs.reset(chain.get());
+ curs.advanceToEnd();
+ EXPECT_EQ(curs.retreatAtMost(5), 5);
+ EXPECT_EQ(curs.totalLength(), 5);
+ EXPECT_EQ(curs.retreatAtMost(10), 10);
+ EXPECT_EQ(curs.totalLength(), 15);
+ EXPECT_EQ(curs.retreatAtMost(10), 5);
+ EXPECT_EQ(curs.totalLength(), 20);
}
-// fbmake opt
-// _bin/folly/experimental/io/test/iobuf_cursor_test -benchmark
-//
-// Benchmark Iters Total t t/iter iter/sec
-// ---------------------------------------------------------------------------
-// rwPrivateCursorBenchmark 100000 142.9 ms 1.429 us 683.5 k
-// rwUnshareCursorBenchmark 100000 309.3 ms 3.093 us 315.7 k
-// cursorBenchmark 100000 741.4 ms 7.414 us 131.7 k
-// skipBenchmark 100000 738.9 ms 7.389 us 132.2 k
-//
-// uname -a:
-//
-// Linux dev2159.snc6.facebook.com 2.6.33-7_fbk15_104e4d0 #1 SMP
-// Tue Oct 19 22:40:30 PDT 2010 x86_64 x86_64 x86_64 GNU/Linux
-//
-// 72GB RAM, 2 CPUs (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz)
-// hyperthreading disabled
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- google::ParseCommandLineFlags(&argc, &argv, true);
-
- auto ret = RUN_ALL_TESTS();
-
- if (ret == 0 && FLAGS_benchmark) {
- iobuf_benchmark = IOBuf::create(benchmark_size);
- iobuf_benchmark->append(benchmark_size);
-
- iobuf_read_benchmark = IOBuf::create(1);
- for (int i = 0; i < benchmark_size; i++) {
- unique_ptr<IOBuf> iobuf2(IOBuf::create(1));
- iobuf2->append(1);
- iobuf_read_benchmark->prependChain(std::move(iobuf2));
- }
+TEST(IOBuf, TestRetreatMulti) {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(10));
+ chain->append(10);
+
+ std::unique_ptr<IOBuf> buf(IOBuf::create(5));
+ buf->append(5);
+ chain->prependChain(std::move(buf));
+
+ buf = IOBuf::create(20);
+ buf->append(20);
+ chain->prependChain(std::move(buf));
+
+ Cursor curs(chain.get());
+ EXPECT_EQ(curs.retreatAtMost(10), 0);
+ EXPECT_THROW(curs.retreat(10), std::out_of_range);
+ curs.reset(chain.get());
+
+ curs.advanceToEnd();
+ curs.retreat(20);
+ EXPECT_EQ(curs.totalLength(), 20);
+ EXPECT_EQ(curs.length(), 20);
+ curs.retreat(1);
+ EXPECT_EQ(curs.totalLength(), 21);
+ EXPECT_EQ(curs.length(), 1);
+ EXPECT_EQ(curs.retreatAtMost(50), 14);
+ EXPECT_EQ(curs.totalLength(), 35);
+
+ curs.advanceToEnd();
+ curs.retreat(30);
+ EXPECT_EQ(curs.totalLength(), 30);
+}
- folly::runBenchmarks();
- }
+TEST(IOBuf, TestRetreatOperators) {
+ std::unique_ptr<IOBuf> chain(IOBuf::create(20));
+ chain->append(20);
+
+ Cursor curs(chain.get());
+ curs.advanceToEnd();
+ curs -= 5;
+ EXPECT_EQ(curs.totalLength(), 5);
- return ret;
+ curs.advanceToEnd();
+ auto retreated = curs - 5;
+ EXPECT_EQ(retreated.totalLength(), 5);
+ EXPECT_EQ(curs.totalLength(), 0);
}