/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 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/Malloc.h>
#include <folly/Range.h>
+#include <cstddef>
+
using folly::fbstring;
using folly::fbvector;
using folly::IOBuf;
}
TEST(IOBuf, Alignment) {
- // max_align_t doesn't exist in gcc 4.6.2
- struct MaxAlign {
- char c;
- } __attribute__((aligned));
- size_t alignment = alignof(MaxAlign);
+ size_t alignment = alignof(std::max_align_t);
std::vector<size_t> sizes {0, 1, 64, 256, 1024, 1 << 10};
for (size_t size : sizes) {
class MoveToFbStringTest
: public ::testing::TestWithParam<std::tr1::tuple<int, int, bool, BufType>> {
protected:
- void SetUp() {
+ void SetUp() override {
elementSize_ = std::tr1::get<0>(GetParam());
elementCount_ = std::tr1::get<1>(GetParam());
shared_ = std::tr1::get<2>(GetParam());
buf->prev()->clear();
iov = buf->getIov();
EXPECT_EQ(count - 3, iov.size());
+
+ // test appending to an existing iovec array
+ iov.clear();
+ const char localBuf[] = "hello";
+ iov.push_back({(void*)localBuf, sizeof(localBuf)});
+ iov.push_back({(void*)localBuf, sizeof(localBuf)});
+ buf->appendToIov(&iov);
+ EXPECT_EQ(count - 1, iov.size());
+ EXPECT_EQ(localBuf, iov[0].iov_base);
+ EXPECT_EQ(localBuf, iov[1].iov_base);
+ // The first two IOBufs were cleared, so the next iov entry
+ // should be the third IOBuf in the chain.
+ EXPECT_EQ(buf->next()->next()->data(), iov[2].iov_base);
}
TEST(IOBuf, move) {
EXPECT_EQ(hash(e), hash(f));
}
+// reserveSlow() had a bug when reallocating the buffer in place. It would
+// preserve old headroom if it's not too much (heuristically) but wouldn't
+// adjust the requested amount of memory to account for that; the end result
+// would be that reserve() would return with less tailroom than requested.
+TEST(IOBuf, ReserveWithHeadroom) {
+ // This is assuming jemalloc, where we know that 4096 and 8192 bytes are
+ // valid (and consecutive) allocation sizes. We're hoping that our
+ // 4096-byte buffer can be expanded in place to 8192 (in practice, this
+ // usually happens).
+ const char data[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";
+ constexpr size_t reservedSize = 24; // sizeof(SharedInfo)
+ // chosen carefully so that the buffer is exactly 4096 bytes
+ IOBuf buf(IOBuf::CREATE, 4096 - reservedSize);
+ buf.advance(10);
+ memcpy(buf.writableData(), data, sizeof(data));
+ buf.append(sizeof(data));
+ EXPECT_EQ(sizeof(data), buf.length());
+
+ // Grow the buffer (hopefully in place); this would incorrectly reserve
+ // the 10 bytes of headroom, giving us 10 bytes less than requested.
+ size_t tailroom = 8192 - reservedSize - sizeof(data);
+ buf.reserve(0, tailroom);
+ EXPECT_LE(tailroom, buf.tailroom());
+ EXPECT_EQ(sizeof(data), buf.length());
+ EXPECT_EQ(0, memcmp(data, buf.data(), sizeof(data)));
+}
+
+TEST(IOBuf, CopyConstructorAndAssignmentOperator) {
+ auto buf = IOBuf::create(4096);
+ append(buf, "hello world");
+ auto buf2 = IOBuf::create(4096);
+ append(buf2, " goodbye");
+ buf->prependChain(std::move(buf2));
+ EXPECT_FALSE(buf->isShared());
+
+ {
+ auto copy = *buf;
+ EXPECT_TRUE(buf->isShared());
+ EXPECT_TRUE(copy.isShared());
+ EXPECT_EQ((void*)buf->data(), (void*)copy.data());
+ EXPECT_NE(buf->next(), copy.next()); // actually different buffers
+
+ auto copy2 = *buf;
+ copy2.coalesce();
+ EXPECT_TRUE(buf->isShared());
+ EXPECT_TRUE(copy.isShared());
+ EXPECT_FALSE(copy2.isShared());
+
+ auto p = reinterpret_cast<const char*>(copy2.data());
+ EXPECT_EQ("hello world goodbye", std::string(p, copy2.length()));
+ }
+
+ EXPECT_FALSE(buf->isShared());
+
+ {
+ folly::IOBuf newBuf(folly::IOBuf::CREATE, 4096);
+ EXPECT_FALSE(newBuf.isShared());
+
+ auto newBufCopy = newBuf;
+ EXPECT_TRUE(newBuf.isShared());
+ EXPECT_TRUE(newBufCopy.isShared());
+
+ newBufCopy = *buf;
+ EXPECT_TRUE(buf->isShared());
+ EXPECT_FALSE(newBuf.isShared());
+ EXPECT_TRUE(newBufCopy.isShared());
+ }
+
+ EXPECT_FALSE(buf->isShared());
+}
+
+namespace {
+// Use with string literals only
+std::unique_ptr<IOBuf> wrap(const char* str) {
+ return IOBuf::wrapBuffer(str, strlen(str));
+}
+
+std::unique_ptr<IOBuf> copy(const char* str) {
+ // At least 1KiB of tailroom, to ensure an external buffer
+ return IOBuf::copyBuffer(str, strlen(str), 0, 1024);
+}
+
+std::string toString(const folly::IOBuf& buf) {
+ std::string result;
+ result.reserve(buf.computeChainDataLength());
+ for (auto& b : buf) {
+ result.append(reinterpret_cast<const char*>(b.data()), b.size());
+ }
+ return result;
+}
+
+char* writableStr(folly::IOBuf& buf) {
+ return reinterpret_cast<char*>(buf.writableData());
+}
+
+} // namespace
+
+TEST(IOBuf, Managed) {
+ auto hello = "hello";
+ auto buf1UP = wrap(hello);
+ auto buf1 = buf1UP.get();
+ EXPECT_FALSE(buf1->isManagedOne());
+ auto buf2UP = copy("world");
+ auto buf2 = buf2UP.get();
+ EXPECT_TRUE(buf2->isManagedOne());
+ auto buf3UP = wrap(hello);
+ auto buf3 = buf3UP.get();
+ auto buf4UP = buf2->clone();
+ auto buf4 = buf4UP.get();
+
+ // buf1 and buf3 share the same memory (but are unmanaged)
+ EXPECT_FALSE(buf1->isManagedOne());
+ EXPECT_FALSE(buf3->isManagedOne());
+ EXPECT_TRUE(buf1->isSharedOne());
+ EXPECT_TRUE(buf3->isSharedOne());
+ EXPECT_EQ(buf1->data(), buf3->data());
+
+ // buf2 and buf4 share the same memory (but are managed)
+ EXPECT_TRUE(buf2->isManagedOne());
+ EXPECT_TRUE(buf4->isManagedOne());
+ EXPECT_TRUE(buf2->isSharedOne());
+ EXPECT_TRUE(buf4->isSharedOne());
+ EXPECT_EQ(buf2->data(), buf4->data());
+
+ buf1->prependChain(std::move(buf2UP));
+ buf1->prependChain(std::move(buf3UP));
+ buf1->prependChain(std::move(buf4UP));
+
+ EXPECT_EQ("helloworldhelloworld", toString(*buf1));
+ EXPECT_FALSE(buf1->isManaged());
+
+ buf1->makeManaged();
+ EXPECT_TRUE(buf1->isManaged());
+
+ // buf1 and buf3 are now unshared (because they were unmanaged)
+ EXPECT_TRUE(buf1->isManagedOne());
+ EXPECT_TRUE(buf3->isManagedOne());
+ EXPECT_FALSE(buf1->isSharedOne());
+ EXPECT_FALSE(buf3->isSharedOne());
+ EXPECT_NE(buf1->data(), buf3->data());
+
+ // buf2 and buf4 are still shared
+ EXPECT_TRUE(buf2->isManagedOne());
+ EXPECT_TRUE(buf4->isManagedOne());
+ EXPECT_TRUE(buf2->isSharedOne());
+ EXPECT_TRUE(buf4->isSharedOne());
+ EXPECT_EQ(buf2->data(), buf4->data());
+
+ // And verify that the truth is what we expect: modify a byte in buf1 and
+ // buf2, see that the change from buf1 is *not* reflected in buf3, but the
+ // change from buf2 is reflected in buf4.
+ writableStr(*buf1)[0] = 'j';
+ writableStr(*buf2)[0] = 'x';
+ EXPECT_EQ("jelloxorldhelloxorld", toString(*buf1));
+}
+
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);