Fill buffer before allocating more
[folly.git] / folly / io / test / IOBufCursorTest.cpp
index 08cb23d44eba9fabe18a31ec90dcdccc09f486a9..89e12052d75d3e9b8e335b96a4493fcb732f29c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 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.
@@ -19,8 +19,9 @@
 #include <folly/Format.h>
 #include <folly/Range.h>
 #include <folly/io/Cursor.h>
-
-#include <gtest/gtest.h>
+#include <folly/portability/GTest.h>
+#include <numeric>
+#include <vector>
 
 using folly::ByteRange;
 using folly::format;
@@ -47,15 +48,15 @@ TEST(IOBuf, RWCursor) {
   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) {
@@ -452,8 +453,8 @@ TEST(IOBuf, Appender) {
   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());
@@ -476,7 +477,7 @@ TEST(IOBuf, Printf) {
              "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(), 1);
+  EXPECT_GE(head.prev()->tailroom(), 1u);
   EXPECT_EQ(0, *head.prev()->tail());
 
   EXPECT_EQ("test32this string is longer than our original "
@@ -516,10 +517,10 @@ TEST(IOBuf, QueueAppender) {
   }
 
   // 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());
 
@@ -531,6 +532,59 @@ TEST(IOBuf, QueueAppender) {
   EXPECT_THROW({cursor.readBE<uint32_t>();}, std::out_of_range);
 }
 
+TEST(IOBuf, QueueAppenderPushAtMostFillBuffer) {
+  folly::IOBufQueue queue;
+  // There should be a goodMallocSize between 125 and 1000
+  QueueAppender appender{&queue, 125};
+  std::vector<uint8_t> data;
+  data.resize(1000);
+  std::iota(data.begin(), data.end(), 0);
+  // Add 100 byte
+  appender.pushAtMost(data.data(), 100);
+  // Add 900 bytes
+  appender.pushAtMost(data.data() + 100, data.size() - 100);
+  const auto buf = queue.front();
+  // Should fill the current buffer before adding another
+  EXPECT_LE(2, buf->countChainElements());
+  EXPECT_EQ(0, buf->tailroom());
+  EXPECT_LE(125, buf->length());
+  EXPECT_EQ(1000, buf->computeChainDataLength());
+  const StringPiece sp{(const char*)data.data(), data.size()};
+  EXPECT_EQ(sp, toString(*buf));
+}
+
+TEST(IOBuf, QueueAppenderInsertOwn) {
+  auto buf = IOBuf::create(10);
+  folly::IOBufQueue queue;
+  QueueAppender appender{&queue, 128};
+  appender.insert(std::move(buf));
+
+  std::vector<uint8_t> data;
+  data.resize(256);
+  std::iota(data.begin(), data.end(), 0);
+  appender.pushAtMost(folly::range(data));
+  // Buffer is owned, so we should write to it
+  EXPECT_LE(2, queue.front()->countChainElements());
+  EXPECT_EQ(0, queue.front()->tailroom());
+  const StringPiece sp{(const char*)data.data(), data.size()};
+  EXPECT_EQ(sp, toString(*queue.front()));
+}
+
+TEST(IOBuf, QueueAppenderInsertClone) {
+  IOBuf buf{IOBuf::CREATE, 100};
+  folly::IOBufQueue queue;
+  QueueAppender appender{&queue, 100};
+  // Buffer is shared, so we create a new buffer to write to
+  appender.insert(buf);
+  uint8_t x = 42;
+  appender.pushAtMost(&x, 1);
+  EXPECT_EQ(2, queue.front()->countChainElements());
+  EXPECT_EQ(0, queue.front()->length());
+  EXPECT_LT(0, queue.front()->tailroom());
+  EXPECT_EQ(1, queue.front()->next()->length());
+  EXPECT_EQ(x, queue.front()->next()->data()[0]);
+}
+
 TEST(IOBuf, CursorOperators) {
   // Test operators on a single-item chain
   {
@@ -863,3 +917,118 @@ TEST(IOBuf, ReadWhileTrue) {
     EXPECT_TRUE(curs.isAtEnd());
   }
 }
+
+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);
+
+  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());
+}
+
+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);
+}
+
+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);
+}
+
+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);
+
+  curs.advanceToEnd();
+  auto retreated = curs - 5;
+  EXPECT_EQ(retreated.totalLength(), 5);
+  EXPECT_EQ(curs.totalLength(), 0);
+}