Commit 53d4209f authored by Tudor Bosman's avatar Tudor Bosman

Add IOBufQueue::prepend, fix bug in IOBuf::prepend.

Summary:
IOBuf::prepend needs to increment length_.

Added IOBufQueue::prepend, which uses the headroom in the first buffer
instead of growing the queue at the head.

Test Plan: tests added

Reviewed By: simpkins@fb.com

FB internal diff: D513676
parent b384b6bc
...@@ -497,6 +497,7 @@ class IOBuf { ...@@ -497,6 +497,7 @@ class IOBuf {
void prepend(uint32_t amount) { void prepend(uint32_t amount) {
CHECK(amount <= headroom()); CHECK(amount <= headroom());
data_ -= amount; data_ -= amount;
length_ += amount;
} }
/** /**
......
...@@ -69,6 +69,37 @@ IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) { ...@@ -69,6 +69,37 @@ IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) {
return *this; return *this;
} }
std::pair<void*, uint32_t>
IOBufQueue::headroom() {
if (head_) {
return std::make_pair(head_->writableBuffer(), head_->headroom());
} else {
return std::make_pair(nullptr, 0);
}
}
void
IOBufQueue::markPrepended(uint32_t n) {
if (n == 0) {
return;
}
assert(head_);
head_->prepend(n);
if (options_.cacheChainLength) {
chainLength_ += n;
}
}
void
IOBufQueue::prepend(const void* buf, uint32_t n) {
auto p = headroom();
if (n > p.second) {
throw std::overflow_error("Not enough room to prepend");
}
memcpy(static_cast<char*>(p.first) + p.second - n, buf, n);
markPrepended(n);
}
void void
IOBufQueue::append(unique_ptr<IOBuf>&& buf) { IOBufQueue::append(unique_ptr<IOBuf>&& buf) {
if (!buf) { if (!buf) {
......
...@@ -28,6 +28,9 @@ namespace folly { ...@@ -28,6 +28,9 @@ namespace folly {
* An IOBufQueue encapsulates a chain of IOBufs and provides * An IOBufQueue encapsulates a chain of IOBufs and provides
* convenience functions to append data to the back of the chain * convenience functions to append data to the back of the chain
* and remove data from the front. * and remove data from the front.
*
* You may also prepend data into the headroom of the first buffer in the
* chain, if any.
*/ */
class IOBufQueue { class IOBufQueue {
public: public:
...@@ -48,6 +51,22 @@ class IOBufQueue { ...@@ -48,6 +51,22 @@ class IOBufQueue {
explicit IOBufQueue(const Options& options = Options()); explicit IOBufQueue(const Options& options = Options());
/**
* Return a space to prepend bytes and the amount of headroom available.
*/
std::pair<void*, uint32_t> headroom();
/**
* Indicate that n bytes from the headroom have been used.
*/
void markPrepended(uint32_t n);
/**
* Prepend an existing range; throws std::overflow_error if not enough
* room.
*/
void prepend(const void* buf, uint32_t n);
/** /**
* Add a buffer or buffer chain to the end of this queue. The * Add a buffer or buffer chain to the end of this queue. The
* queue takes ownership of buf. * queue takes ownership of buf.
......
...@@ -239,6 +239,25 @@ TEST(IOBufQueue, trim) { ...@@ -239,6 +239,25 @@ TEST(IOBufQueue, trim) {
checkConsistency(queue); checkConsistency(queue);
} }
TEST(IOBufQueue, Prepend) {
folly::IOBufQueue queue;
auto buf = folly::IOBuf::create(10);
buf->advance(5);
queue.append(std::move(buf));
queue.append(SCL(" World"));
queue.prepend(SCL("Hello"));
EXPECT_THROW(queue.prepend(SCL("x")), std::overflow_error);
auto out = queue.move();
out->coalesce();
EXPECT_EQ("Hello World",
StringPiece(reinterpret_cast<const char*>(out->data()),
out->length()));
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true); google::ParseCommandLineFlags(&argc, &argv, true);
......
...@@ -35,6 +35,12 @@ void append(std::unique_ptr<IOBuf>& buf, StringPiece str) { ...@@ -35,6 +35,12 @@ void append(std::unique_ptr<IOBuf>& buf, StringPiece str) {
buf->append(str.size()); buf->append(str.size());
} }
void prepend(std::unique_ptr<IOBuf>& buf, StringPiece str) {
EXPECT_LE(str.size(), buf->headroom());
memcpy(buf->writableData() - str.size(), str.data(), str.size());
buf->prepend(str.size());
}
TEST(IOBuf, Simple) { TEST(IOBuf, Simple) {
unique_ptr<IOBuf> buf(IOBuf::create(100)); unique_ptr<IOBuf> buf(IOBuf::create(100));
uint32_t cap = buf->capacity(); uint32_t cap = buf->capacity();
...@@ -43,13 +49,19 @@ TEST(IOBuf, Simple) { ...@@ -43,13 +49,19 @@ TEST(IOBuf, Simple) {
EXPECT_EQ(0, buf->length()); EXPECT_EQ(0, buf->length());
EXPECT_EQ(cap, buf->tailroom()); EXPECT_EQ(cap, buf->tailroom());
append(buf, "hello"); append(buf, "world");
buf->advance(10); buf->advance(10);
EXPECT_EQ(10, buf->headroom()); EXPECT_EQ(10, buf->headroom());
EXPECT_EQ(5, buf->length()); EXPECT_EQ(5, buf->length());
EXPECT_EQ(cap - 15, buf->tailroom()); EXPECT_EQ(cap - 15, buf->tailroom());
prepend(buf, "hello ");
EXPECT_EQ(4, buf->headroom());
EXPECT_EQ(11, buf->length());
EXPECT_EQ(cap - 15, buf->tailroom());
const char* p = reinterpret_cast<const char*>(buf->data()); const char* p = reinterpret_cast<const char*>(buf->data());
EXPECT_EQ("hello", std::string(p, buf->length())); EXPECT_EQ("hello world", std::string(p, buf->length()));
buf->clear(); buf->clear();
EXPECT_EQ(0, buf->headroom()); EXPECT_EQ(0, buf->headroom());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment