Commit ba1a4c1b authored by Giuseppe Ottaviano's avatar Giuseppe Ottaviano Committed by Facebook GitHub Bot

Prevent IOBufQueue::append() from packing into a shared tail

Summary:
If a shared `IOBuf` is appended to `IOBufQueue` its tail is not considered writable, but we may write into it in the packing logic.

Fix that, and share the implementation between the pointer and value versions.

Reviewed By: luciang

Differential Revision: D31890708

fbshipit-source-id: 2b590cbcab7028af9421d29babb760869833bc65
parent cd49bf89
...@@ -31,28 +31,36 @@ const size_t MIN_ALLOC_SIZE = 2000; ...@@ -31,28 +31,36 @@ const size_t MIN_ALLOC_SIZE = 2000;
const size_t MAX_ALLOC_SIZE = 8000; const size_t MAX_ALLOC_SIZE = 8000;
/** /**
* Convenience function to append chain src to chain dst. * Convenience functions to append chain src to chain dst.
*/ */
void appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) { template <class Src, class Next>
if (dst == nullptr) { void packInto(IOBuf* tail, Src& src, Next next) {
dst = std::move(src); if (tail->isSharedOne()) {
} else { return;
IOBuf* tail = dst->prev(); }
if (pack) {
// Copy up to kMaxPackCopy bytes if we can free buffers; this helps // Copy up to kMaxPackCopy bytes if we can free buffers; this helps reduce
// reduce wastage (the tail's tailroom and the head's headroom) when // waste (the tail's tailroom and the head's headroom) when joining two
// joining two IOBufQueues together. // IOBufQueues together.
size_t copyRemaining = folly::IOBufQueue::kMaxPackCopy; size_t copyRemaining = folly::IOBufQueue::kMaxPackCopy;
std::size_t n; std::size_t n;
while (src && (n = src->length()) <= copyRemaining && while (src && (n = src->length()) <= copyRemaining && n <= tail->tailroom()) {
n <= tail->tailroom()) {
if (n > 0) { if (n > 0) {
memcpy(tail->writableTail(), src->data(), n); memcpy(tail->writableTail(), src->data(), n);
tail->append(n); tail->append(n);
copyRemaining -= n; copyRemaining -= n;
} }
src = src->pop(); src = next(std::move(src));
} }
}
void appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) {
if (dst == nullptr) {
dst = std::move(src);
} else {
IOBuf* tail = dst->prev();
if (pack) {
packInto(tail, src, [](auto&& cur) { return cur->pop(); });
} }
if (src) { if (src) {
tail->appendChain(std::move(src)); tail->appendChain(std::move(src));
...@@ -164,22 +172,14 @@ void IOBufQueue::append( ...@@ -164,22 +172,14 @@ void IOBufQueue::append(
chainLength_ += buf.computeChainDataLength(); chainLength_ += buf.computeChainDataLength();
} }
size_t copyRemaining = kMaxPackCopy;
std::size_t n;
const folly::IOBuf* src = &buf;
folly::IOBuf* tail = head_->prev(); folly::IOBuf* tail = head_->prev();
while ((n = src->length()) <= copyRemaining && n <= tail->tailroom()) { const folly::IOBuf* src = &buf;
if (n > 0) { packInto(tail, src, [&](auto&& cur) {
memcpy(tail->writableTail(), src->data(), n); auto next = cur->next();
tail->append(n); return next != &buf ? next : nullptr;
copyRemaining -= n; });
} if (!src) {
src = src->next(); return; // Consumed full input.
// Consumed full input.
if (src == &buf) {
return;
}
} }
// Clone the rest. // Clone the rest.
......
...@@ -42,8 +42,9 @@ struct Initializer { ...@@ -42,8 +42,9 @@ struct Initializer {
}; };
Initializer initializer; Initializer initializer;
unique_ptr<IOBuf> stringToIOBuf(const char* s, size_t len) { unique_ptr<IOBuf> stringToIOBuf(
unique_ptr<IOBuf> buf = IOBuf::create(len); const char* s, size_t len, size_t tailroom = 0) {
unique_ptr<IOBuf> buf = IOBuf::create(len + tailroom);
memcpy(buf->writableTail(), s, len); memcpy(buf->writableTail(), s, len);
buf->append(len); buf->append(len);
return buf; return buf;
...@@ -601,3 +602,14 @@ TEST(IOBufQueue, ReuseTail) { ...@@ -601,3 +602,14 @@ TEST(IOBufQueue, ReuseTail) {
} }
} }
} }
TEST(IOBufQueue, PackWithSharedTail) {
auto buf = stringToIOBuf("Hello ", 6, 10);
IOBufQueue queue;
queue.append(buf->clone());
*buf->writableTail() = 'X';
buf->append(1);
queue.append(stringToIOBuf("world", 5), /* pack */ true);
// buf is shared, packing should not modify it.
EXPECT_EQ(buf->data()[6], 'X');
}
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