Commit 51f6dea4 authored by Hans Fugal's avatar Hans Fugal Committed by Owen Yamauchi

IOBuf::getIov

Summary:
Generate an `fbvector` of `struct iovec` suitable for using with `writev` or
`sendmsg`.

This code is pretty straightforward, but Adam pointed out that something along
these lines has already been done in thrift, so I followed that code closely.
http://fburl.com/11586814

Test Plan:
fbmake runtests

I am using also this in a prototype and it's working there.

Reviewed By: agartrell@fb.com

FB internal diff: D744055
parent 33a4bac6
......@@ -643,4 +643,18 @@ IOBuf::Iterator IOBuf::cend() const {
return Iterator(nullptr, nullptr);
}
folly::fbvector<struct iovec> IOBuf::getIov() const {
folly::fbvector<struct iovec> iov;
iov.reserve(countChainElements());
IOBuf const* p = this;
do {
// some code can get confused by empty iovs, so skip them
if (p->length() > 0) {
iov.push_back({(void*)p->data(), p->length()});
}
p = p->next();
} while (p != this);
return iov;
}
} // folly
......@@ -25,12 +25,14 @@
#include <cstring>
#include <memory>
#include <limits>
#include <sys/uio.h>
#include <type_traits>
#include <boost/iterator/iterator_facade.hpp>
#include "folly/FBString.h"
#include "folly/Range.h"
#include "folly/FBVector.h"
namespace folly {
......@@ -922,6 +924,17 @@ class IOBuf {
*/
std::unique_ptr<IOBuf> cloneOne() const;
/**
* Return an iovector suitable for e.g. writev()
*
* auto iov = buf->getIov();
* auto xfer = writev(fd, iov.data(), iov.size());
*
* Naturally, the returned iovector is invalid if you modify the buffer
* chain.
*/
folly::fbvector<struct iovec> getIov() const;
// Overridden operator new and delete.
// These directly use malloc() and free() to allocate the space for IOBuf
// objects. This is needed since IOBuf::create() manually uses malloc when
......
......@@ -28,6 +28,7 @@
#include "folly/Range.h"
using folly::fbstring;
using folly::fbvector;
using folly::IOBuf;
using folly::TypedIOBuf;
using folly::StringPiece;
......@@ -764,6 +765,51 @@ INSTANTIATE_TEST_CASE_P(
::testing::Values(1, 2, 10), // element count
::testing::Bool())); // shared
TEST(IOBuf, getIov) {
uint32_t fillSeed = 0xdeadbeef;
boost::mt19937 gen(fillSeed);
size_t len = 4096;
size_t count = 32;
auto buf = IOBuf::create(len + 1);
buf->append(rand() % len + 1);
fillBuf(buf.get(), gen);
for (size_t i = 0; i < count - 1; i++) {
auto buf2 = IOBuf::create(len + 1);
buf2->append(rand() % len + 1);
fillBuf(buf2.get(), gen);
buf->prependChain(std::move(buf2));
}
EXPECT_EQ(count, buf->countChainElements());
auto iov = buf->getIov();
EXPECT_EQ(count, iov.size());
IOBuf const* p = buf.get();
for (size_t i = 0; i < count; i++, p = p->next()) {
EXPECT_EQ(p->data(), iov[i].iov_base);
EXPECT_EQ(p->length(), iov[i].iov_len);
}
// an empty buf should be skipped in the iov.
buf->next()->clear();
iov = buf->getIov();
EXPECT_EQ(count - 1, iov.size());
EXPECT_EQ(buf->next()->next()->data(), iov[1].iov_base);
// same for the first one being empty
buf->clear();
iov = buf->getIov();
EXPECT_EQ(count - 2, iov.size());
EXPECT_EQ(buf->next()->next()->data(), iov[0].iov_base);
// and the last one
buf->prev()->clear();
iov = buf->getIov();
EXPECT_EQ(count - 3, iov.size());
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
......
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