Commit 7fb1e3d7 authored by Tudor Bosman's avatar Tudor Bosman Committed by Jordan DeLong

graduate IOBuf out of folly/experimental

Summary: Move IOBuf and related code to folly/io.

Test Plan: fbconfig -r folly && fbmake runtests_opt, fbconfig unicorn/test && fbmake opt

Reviewed By: andrewcox@fb.com

FB internal diff: D678331
parent c8be994e
/*
* Copyright 2012 Facebook, Inc.
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -33,7 +33,7 @@
#include "folly/Conv.h"
#include "folly/ScopeGuard.h"
#include "folly/String.h"
#include "folly/experimental/io/Cursor.h"
#include "folly/io/Cursor.h"
extern char** environ;
......
/*
* Copyright 2012 Facebook, Inc.
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -66,7 +66,7 @@
#include <boost/operators.hpp>
#include <boost/noncopyable.hpp>
#include "folly/experimental/io/IOBufQueue.h"
#include "folly/io/IOBufQueue.h"
#include "folly/MapUtil.h"
#include "folly/Portability.h"
#include "folly/Range.h"
......
/*
* Copyright 2012 Facebook, Inc.
* Copyright 2013 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,7 +19,7 @@
#include "folly/experimental/File.h"
#include "folly/experimental/Gen.h"
#include "folly/experimental/io/IOBuf.h"
#include "folly/io/IOBuf.h"
namespace folly {
namespace gen {
......
/*
* Copyright 2012 Facebook, Inc.
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,7 +18,7 @@
#error This file may only be included from folly/experimental/StringGen.h
#endif
#include "folly/experimental/io/IOBuf.h"
#include "folly/io/IOBuf.h"
namespace folly {
namespace gen {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "folly/io/IOBufQueue.h"
#include <string.h>
#include <stdexcept>
using std::make_pair;
using std::pair;
using std::unique_ptr;
namespace {
using folly::IOBuf;
const size_t MIN_ALLOC_SIZE = 2000;
const size_t MAX_ALLOC_SIZE = 8000; // Must fit within a uint32_t
/**
* Convenience function to append chain src to chain dst.
*/
void
appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src) {
if (dst == NULL) {
dst = std::move(src);
} else {
dst->prev()->appendChain(std::move(src));
}
}
} // anonymous namespace
namespace folly {
IOBufQueue::IOBufQueue(const Options& options)
: options_(options),
chainLength_(0) {
}
IOBufQueue::IOBufQueue(IOBufQueue&& other)
: options_(other.options_),
chainLength_(other.chainLength_),
head_(std::move(other.head_)) {
other.chainLength_ = 0;
}
IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) {
if (&other != this) {
options_ = other.options_;
chainLength_ = other.chainLength_;
head_ = std::move(other.head_);
other.chainLength_ = 0;
}
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
IOBufQueue::append(unique_ptr<IOBuf>&& buf) {
if (!buf) {
return;
}
if (options_.cacheChainLength) {
chainLength_ += buf->computeChainDataLength();
}
appendToChain(head_, std::move(buf));
}
void
IOBufQueue::append(IOBufQueue& other) {
if (!other.head_) {
return;
}
if (options_.cacheChainLength) {
if (other.options_.cacheChainLength) {
chainLength_ += other.chainLength_;
} else {
chainLength_ += other.head_->computeChainDataLength();
}
}
appendToChain(head_, std::move(other.head_));
other.chainLength_ = 0;
}
void
IOBufQueue::append(const void* buf, size_t len) {
auto src = static_cast<const uint8_t*>(buf);
while (len != 0) {
if ((head_ == NULL) || head_->prev()->isSharedOne() ||
(head_->prev()->tailroom() == 0)) {
appendToChain(head_, std::move(
IOBuf::create(std::max(MIN_ALLOC_SIZE,
std::min(len, MAX_ALLOC_SIZE)))));
}
IOBuf* last = head_->prev();
uint32_t copyLen = std::min(len, (size_t)last->tailroom());
memcpy(last->writableTail(), src, copyLen);
src += copyLen;
last->append(copyLen);
if (options_.cacheChainLength) {
chainLength_ += copyLen;
}
len -= copyLen;
}
}
void
IOBufQueue::wrapBuffer(const void* buf, size_t len, uint32_t blockSize) {
auto src = static_cast<const uint8_t*>(buf);
while (len != 0) {
size_t n = std::min(len, size_t(blockSize));
append(IOBuf::wrapBuffer(src, n));
src += n;
len -= n;
}
}
pair<void*,uint32_t>
IOBufQueue::preallocate(uint32_t min, uint32_t newAllocationSize,
uint32_t max) {
if (head_ != NULL) {
// If there's enough space left over at the end of the queue, use that.
IOBuf* last = head_->prev();
if (!last->isSharedOne()) {
uint32_t avail = last->tailroom();
if (avail >= min) {
return make_pair(last->writableTail(), std::min(max, avail));
}
}
}
// Allocate a new buffer of the requested max size.
unique_ptr<IOBuf> newBuf(IOBuf::create(std::max(min, newAllocationSize)));
appendToChain(head_, std::move(newBuf));
IOBuf* last = head_->prev();
return make_pair(last->writableTail(),
std::min(max, last->tailroom()));
}
void
IOBufQueue::postallocate(uint32_t n) {
head_->prev()->append(n);
if (options_.cacheChainLength) {
chainLength_ += n;
}
}
unique_ptr<IOBuf>
IOBufQueue::split(size_t n) {
unique_ptr<IOBuf> result;
while (n != 0) {
if (head_ == NULL) {
throw std::underflow_error(
"Attempt to remove more bytes than are present in IOBufQueue");
} else if (head_->length() <= n) {
n -= head_->length();
if (options_.cacheChainLength) {
chainLength_ -= head_->length();
}
unique_ptr<IOBuf> remainder = head_->pop();
appendToChain(result, std::move(head_));
head_ = std::move(remainder);
} else {
unique_ptr<IOBuf> clone = head_->cloneOne();
clone->trimEnd(clone->length() - n);
appendToChain(result, std::move(clone));
head_->trimStart(n);
if (options_.cacheChainLength) {
chainLength_ -= n;
}
break;
}
}
return std::move(result);
}
void IOBufQueue::trimStart(size_t amount) {
while (amount > 0) {
if (!head_) {
throw std::underflow_error(
"Attempt to trim more bytes than are present in IOBufQueue");
}
if (head_->length() > amount) {
head_->trimStart(amount);
if (options_.cacheChainLength) {
chainLength_ -= amount;
}
break;
}
amount -= head_->length();
if (options_.cacheChainLength) {
chainLength_ -= head_->length();
}
head_ = head_->pop();
}
}
void IOBufQueue::trimEnd(size_t amount) {
while (amount > 0) {
if (!head_) {
throw std::underflow_error(
"Attempt to trim more bytes than are present in IOBufQueue");
}
if (head_->prev()->length() > amount) {
head_->prev()->trimEnd(amount);
if (options_.cacheChainLength) {
chainLength_ -= amount;
}
break;
}
amount -= head_->prev()->length();
if (options_.cacheChainLength) {
chainLength_ -= head_->prev()->length();
}
unique_ptr<IOBuf> b = head_->prev()->unlink();
// Null queue if we unlinked the head.
if (b.get() == head_.get()) {
head_.reset();
}
}
}
} // folly
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_IO_IOBUF_QUEUE_H
#define FOLLY_IO_IOBUF_QUEUE_H
#include "folly/io/IOBuf.h"
#include <stdexcept>
#include <string>
namespace folly {
/**
* An IOBufQueue encapsulates a chain of IOBufs and provides
* convenience functions to append data to the back of the chain
* 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 {
public:
struct Options {
Options() : cacheChainLength(false) { }
bool cacheChainLength;
};
/**
* Commonly used Options, currently the only possible value other than
* the default.
*/
static Options cacheChainLength() {
Options options;
options.cacheChainLength = true;
return 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
* queue takes ownership of buf.
*/
void append(std::unique_ptr<folly::IOBuf>&& buf);
/**
* Add a queue to the end of this queue. The queue takes ownership of
* all buffers from the other queue.
*/
void append(IOBufQueue& other);
void append(IOBufQueue&& other) {
append(other); // call lvalue reference overload, above
}
/**
* Copy len bytes, starting at buf, to the end of this queue.
* The caller retains ownership of the source data.
*/
void append(const void* buf, size_t len);
/**
* Copy a string to the end of this queue.
* The caller retains ownership of the source data.
*/
void append(const std::string& buf) {
append(buf.data(), buf.length());
}
/**
* Append a chain of IOBuf objects that point to consecutive regions
* within buf.
*
* Just like IOBuf::wrapBuffer, this should only be used when the caller
* knows ahead of time and can ensure that all IOBuf objects that will point
* to this buffer will be destroyed before the buffer itself is destroyed;
* all other caveats from wrapBuffer also apply.
*
* Every buffer except for the last will wrap exactly blockSize bytes.
* Importantly, this method may be used to wrap buffers larger than 4GB.
*/
void wrapBuffer(const void* buf, size_t len,
uint32_t blockSize=(1U << 31)); // default block size: 2GB
/**
* Obtain a writable block of contiguous bytes at the end of this
* queue, allocating more space if necessary. The amount of space
* reserved will be at least min. If min contiguous space is not
* available at the end of the queue, and IOBuf with size newAllocationSize
* is appended to the chain and returned. The actual available space
* may be larger than newAllocationSize, but will be truncated to max,
* if specified.
*
* If the caller subsequently writes anything into the returned space,
* it must call the postallocate() method.
*
* @return The starting address of the block and the length in bytes.
*
* @note The point of the preallocate()/postallocate() mechanism is
* to support I/O APIs such as Thrift's TAsyncSocket::ReadCallback
* that request a buffer from the application and then, in a later
* callback, tell the application how much of the buffer they've
* filled with data.
*/
std::pair<void*,uint32_t> preallocate(
uint32_t min, uint32_t newAllocationSize,
uint32_t max = std::numeric_limits<uint32_t>::max());
/**
* Tell the queue that the caller has written data into the first n
* bytes provided by the previous preallocate() call.
*
* @note n should be less than or equal to the size returned by
* preallocate(). If n is zero, the caller may skip the call
* to postallocate(). If n is nonzero, the caller must not
* invoke any other non-const methods on this IOBufQueue between
* the call to preallocate and the call to postallocate().
*/
void postallocate(uint32_t n);
/**
* Obtain a writable block of n contiguous bytes, allocating more space
* if necessary, and mark it as used. The caller can fill it later.
*/
void* allocate(uint32_t n) {
void* p = preallocate(n, n).first;
postallocate(n);
return p;
}
/**
* Split off the first n bytes of the queue into a separate IOBuf chain,
* and transfer ownership of the new chain to the caller. The IOBufQueue
* retains ownership of everything after the split point.
*
* @warning If the split point lies in the middle of some IOBuf within
* the chain, this function may, as an implementation detail,
* clone that IOBuf.
*
* @throws std::underflow_error if n exceeds the number of bytes
* in the queue.
*/
std::unique_ptr<folly::IOBuf> split(size_t n);
/**
* Similar to IOBuf::trimStart, but works on the whole queue. Will
* pop off buffers that have been completely trimmed.
*/
void trimStart(size_t amount);
/**
* Similar to IOBuf::trimEnd, but works on the whole queue. Will
* pop off buffers that have been completely trimmed.
*/
void trimEnd(size_t amount);
/**
* Transfer ownership of the queue's entire IOBuf chain to the caller.
*/
std::unique_ptr<folly::IOBuf> move() {
chainLength_ = 0;
return std::move(head_);
}
/**
* Access
*/
const folly::IOBuf* front() const {
return head_.get();
}
/**
* Total chain length, only valid if cacheLength was specified in the
* constructor.
*/
size_t chainLength() const {
if (!options_.cacheChainLength) {
throw std::invalid_argument("IOBufQueue: chain length not cached");
}
return chainLength_;
}
const Options& options() const {
return options_;
}
/** Movable */
IOBufQueue(IOBufQueue&&);
IOBufQueue& operator=(IOBufQueue&&);
private:
static const size_t kChainLengthNotCached = (size_t)-1;
/** Not copyable */
IOBufQueue(const IOBufQueue&) = delete;
IOBufQueue& operator=(const IOBufQueue&) = delete;
Options options_;
size_t chainLength_;
/** Everything that has been appended but not yet discarded or moved out */
std::unique_ptr<folly::IOBuf> head_;
};
} // folly
#endif // FOLLY_IO_IOBUF_QUEUE_H
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_IO_TYPEDIOBUF_H_
#define FOLLY_IO_TYPEDIOBUF_H_
#include <algorithm>
#include <iterator>
#include <type_traits>
#include "folly/io/IOBuf.h"
namespace folly {
/**
* Wrapper class to handle a IOBuf as a typed buffer (to a standard layout
* class).
*
* This class punts on alignment, and assumes that you know what you're doing.
*
* All methods are wrappers around the corresponding IOBuf methods. The
* TypedIOBuf object is stateless, so it's perfectly okay to access the
* underlying IOBuf in between TypedIOBuf method calls.
*/
template <class T>
class TypedIOBuf {
static_assert(std::is_standard_layout<T>::value, "must be standard layout");
public:
typedef T value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef uint32_t size_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
explicit TypedIOBuf(IOBuf* buf) : buf_(buf) { }
IOBuf* ioBuf() {
return buf_;
}
const IOBuf* ioBuf() const {
return buf_;
}
bool empty() const {
return buf_->empty();
}
const T* data() const {
return cast(buf_->data());
}
T* writableData() {
return cast(buf_->writableData());
}
const T* tail() const {
return cast(buf_->tail());
}
T* writableTail() {
return cast(buf_->writableTail());
}
uint32_t length() const {
return sdiv(buf_->length());
}
uint32_t size() const { return length(); }
uint32_t headroom() const {
return sdiv(buf_->headroom());
}
uint32_t tailroom() const {
return sdiv(buf_->tailroom());
}
const T* buffer() const {
return cast(buf_->buffer());
}
T* writableBuffer() {
return cast(buf_->writableBuffer());
}
const T* bufferEnd() const {
return cast(buf_->bufferEnd());
}
uint32_t capacity() const {
return sdiv(buf_->capacity());
}
void advance(uint32_t n) {
buf_->advance(smul(n));
}
void retreat(uint32_t n) {
buf_->retreat(smul(n));
}
void prepend(uint32_t n) {
buf_->prepend(smul(n));
}
void append(uint32_t n) {
buf_->append(smul(n));
}
void trimStart(uint32_t n) {
buf_->trimStart(smul(n));
}
void trimEnd(uint32_t n) {
buf_->trimEnd(smul(n));
}
void clear() {
buf_->clear();
}
void reserve(uint32_t minHeadroom, uint32_t minTailroom) {
buf_->reserve(smul(minHeadroom), smul(minTailroom));
}
void reserve(uint32_t minTailroom) { reserve(0, minTailroom); }
const T* cbegin() const { return data(); }
const T* cend() const { return tail(); }
const T* begin() const { return cbegin(); }
const T* end() const { return cend(); }
T* begin() { return writableData(); }
T* end() { return writableTail(); }
const T& front() const {
assert(!empty());
return *begin();
}
T& front() {
assert(!empty());
return *begin();
}
const T& back() const {
assert(!empty());
return end()[-1];
}
T& back() {
assert(!empty());
return end()[-1];
}
/**
* Simple wrapper to make it easier to treat this TypedIOBuf as an array of
* T.
*/
const T& operator[](ssize_t idx) const {
assert(idx >= 0 && idx < length());
return data()[idx];
}
/**
* Append one element.
*/
void push(const T& data) {
push(&data, &data + 1);
}
void push_back(const T& data) { push(data); }
/**
* Append multiple elements in a sequence; will call distance().
*/
template <class IT>
void push(IT begin, IT end) {
auto n = std::distance(begin, end);
reserve(headroom(), n);
std::copy(begin, end, writableTail());
append(n);
}
// Movable
TypedIOBuf(TypedIOBuf&&) = default;
TypedIOBuf& operator=(TypedIOBuf&&) = default;
private:
// Non-copyable
TypedIOBuf(const TypedIOBuf&) = delete;
TypedIOBuf& operator=(const TypedIOBuf&) = delete;
// cast to T*
static T* cast(uint8_t* p) {
return reinterpret_cast<T*>(p);
}
static const T* cast(const uint8_t* p) {
return reinterpret_cast<const T*>(p);
}
// divide by size
static uint32_t sdiv(uint32_t n) {
return n / sizeof(T);
}
// multiply by size
static uint32_t smul(uint32_t n) {
// In debug mode, check for overflow
assert((uint64_t(n) * sizeof(T)) < (uint64_t(1) << 32));
return n * sizeof(T);
}
IOBuf* buf_;
};
} // namespace folly
#endif /* FOLLY_IO_TYPEDIOBUF_H_ */
This diff is collapsed.
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "folly/io/IOBufQueue.h"
#include "folly/Range.h"
#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include <iostream>
#include <stdexcept>
#include <string.h>
using folly::IOBuf;
using folly::IOBufQueue;
using folly::StringPiece;
using std::pair;
using std::string;
using std::unique_ptr;
// String Comma Length macro for string literals
#define SCL(x) (x), sizeof(x) - 1
namespace {
IOBufQueue::Options clOptions;
struct Initializer {
Initializer() {
clOptions.cacheChainLength = true;
}
};
Initializer initializer;
unique_ptr<IOBuf>
stringToIOBuf(const char* s, uint32_t len) {
unique_ptr<IOBuf> buf = IOBuf::create(len);
memcpy(buf->writableTail(), s, len);
buf->append(len);
return std::move(buf);
}
void checkConsistency(const IOBufQueue& queue) {
if (queue.options().cacheChainLength) {
size_t len = queue.front() ? queue.front()->computeChainDataLength() : 0;
EXPECT_EQ(len, queue.chainLength());
}
}
}
TEST(IOBufQueue, Simple) {
IOBufQueue queue(clOptions);
EXPECT_EQ(NULL, queue.front());
queue.append(SCL(""));
EXPECT_EQ(NULL, queue.front());
queue.append(unique_ptr<IOBuf>());
EXPECT_EQ(NULL, queue.front());
string emptyString;
queue.append(emptyString);
EXPECT_EQ(NULL, queue.front());
}
TEST(IOBufQueue, Append) {
IOBufQueue queue(clOptions);
queue.append(SCL("Hello"));
IOBufQueue queue2(clOptions);
queue2.append(SCL(", "));
queue2.append(SCL("World"));
checkConsistency(queue);
checkConsistency(queue2);
queue.append(queue2.move());
checkConsistency(queue);
checkConsistency(queue2);
const IOBuf* chain = queue.front();
EXPECT_NE((IOBuf*)NULL, chain);
EXPECT_EQ(12, chain->computeChainDataLength());
EXPECT_EQ(NULL, queue2.front());
}
TEST(IOBufQueue, Append2) {
IOBufQueue queue(clOptions);
queue.append(SCL("Hello"));
IOBufQueue queue2(clOptions);
queue2.append(SCL(", "));
queue2.append(SCL("World"));
checkConsistency(queue);
checkConsistency(queue2);
queue.append(queue2);
checkConsistency(queue);
checkConsistency(queue2);
const IOBuf* chain = queue.front();
EXPECT_NE((IOBuf*)NULL, chain);
EXPECT_EQ(12, chain->computeChainDataLength());
EXPECT_EQ(NULL, queue2.front());
}
TEST(IOBufQueue, Split) {
IOBufQueue queue(clOptions);
queue.append(stringToIOBuf(SCL("Hello")));
queue.append(stringToIOBuf(SCL(",")));
queue.append(stringToIOBuf(SCL(" ")));
queue.append(stringToIOBuf(SCL("")));
queue.append(stringToIOBuf(SCL("World")));
checkConsistency(queue);
EXPECT_EQ(12, queue.front()->computeChainDataLength());
unique_ptr<IOBuf> prefix(queue.split(1));
checkConsistency(queue);
EXPECT_EQ(1, prefix->computeChainDataLength());
EXPECT_EQ(11, queue.front()->computeChainDataLength());
prefix = queue.split(2);
checkConsistency(queue);
EXPECT_EQ(2, prefix->computeChainDataLength());
EXPECT_EQ(9, queue.front()->computeChainDataLength());
prefix = queue.split(3);
checkConsistency(queue);
EXPECT_EQ(3, prefix->computeChainDataLength());
EXPECT_EQ(6, queue.front()->computeChainDataLength());
prefix = queue.split(1);
checkConsistency(queue);
EXPECT_EQ(1, prefix->computeChainDataLength());
EXPECT_EQ(5, queue.front()->computeChainDataLength());
prefix = queue.split(5);
checkConsistency(queue);
EXPECT_EQ(5, prefix->computeChainDataLength());
EXPECT_EQ((IOBuf*)NULL, queue.front());
queue.append(stringToIOBuf(SCL("Hello,")));
queue.append(stringToIOBuf(SCL(" World")));
checkConsistency(queue);
bool exceptionFired = false;
EXPECT_THROW({prefix = queue.split(13);}, std::underflow_error);
checkConsistency(queue);
}
TEST(IOBufQueue, Preallocate) {
IOBufQueue queue(clOptions);
queue.append(string("Hello"));
pair<void*,uint32_t> writable = queue.preallocate(2, 64, 64);
checkConsistency(queue);
EXPECT_NE((void*)NULL, writable.first);
EXPECT_LE(2, writable.second);
EXPECT_GE(64, writable.second);
memcpy(writable.first, SCL(", "));
queue.postallocate(2);
checkConsistency(queue);
EXPECT_EQ(7, queue.front()->computeChainDataLength());
queue.append(SCL("World"));
checkConsistency(queue);
EXPECT_EQ(12, queue.front()->computeChainDataLength());
// There are not 2048 bytes available, this will alloc a new buf
writable = queue.preallocate(2048, 4096);
checkConsistency(queue);
EXPECT_LE(2048, writable.second);
// IOBuf allocates more than newAllocationSize, and we didn't cap it
EXPECT_GE(writable.second, 4096);
queue.postallocate(writable.second);
// queue has no empty space, make sure we allocate at least min, even if
// newAllocationSize < min
writable = queue.preallocate(1024, 1, 1024);
checkConsistency(queue);
EXPECT_EQ(1024, writable.second);
}
TEST(IOBufQueue, Wrap) {
IOBufQueue queue(clOptions);
const char* buf = "hello world goodbye";
size_t len = strlen(buf);
queue.wrapBuffer(buf, len, 6);
auto iob = queue.move();
EXPECT_EQ((len - 1) / 6 + 1, iob->countChainElements());
iob->unshare();
iob->coalesce();
EXPECT_EQ(StringPiece(buf),
StringPiece(reinterpret_cast<const char*>(iob->data()),
iob->length()));
}
TEST(IOBufQueue, trim) {
IOBufQueue queue(clOptions);
unique_ptr<IOBuf> a = IOBuf::create(4);
a->append(4);
queue.append(std::move(a));
checkConsistency(queue);
a = IOBuf::create(6);
a->append(6);
queue.append(std::move(a));
checkConsistency(queue);
a = IOBuf::create(8);
a->append(8);
queue.append(std::move(a));
checkConsistency(queue);
a = IOBuf::create(10);
a->append(10);
queue.append(std::move(a));
checkConsistency(queue);
EXPECT_EQ(4, queue.front()->countChainElements());
EXPECT_EQ(28, queue.front()->computeChainDataLength());
EXPECT_EQ(4, queue.front()->length());
queue.trimStart(1);
checkConsistency(queue);
EXPECT_EQ(4, queue.front()->countChainElements());
EXPECT_EQ(27, queue.front()->computeChainDataLength());
EXPECT_EQ(3, queue.front()->length());
queue.trimStart(5);
checkConsistency(queue);
EXPECT_EQ(3, queue.front()->countChainElements());
EXPECT_EQ(22, queue.front()->computeChainDataLength());
EXPECT_EQ(4, queue.front()->length());
queue.trimEnd(1);
checkConsistency(queue);
EXPECT_EQ(3, queue.front()->countChainElements());
EXPECT_EQ(21, queue.front()->computeChainDataLength());
EXPECT_EQ(9, queue.front()->prev()->length());
queue.trimEnd(20);
checkConsistency(queue);
EXPECT_EQ(1, queue.front()->countChainElements());
EXPECT_EQ(1, queue.front()->computeChainDataLength());
EXPECT_EQ(1, queue.front()->prev()->length());
queue.trimEnd(1);
checkConsistency(queue);
EXPECT_EQ(NULL, queue.front());
EXPECT_THROW(queue.trimStart(2), std::underflow_error);
checkConsistency(queue);
EXPECT_THROW(queue.trimEnd(30), std::underflow_error);
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) {
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
}
This diff is collapsed.
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "folly/io/IOBuf.h"
#include <gflags/gflags.h>
#include "folly/Benchmark.h"
#include "folly/io/Cursor.h"
#include <vector>
using folly::IOBuf;
using std::unique_ptr;
using namespace folly::io;
using namespace std;
size_t buf_size = 0;
size_t num_bufs = 0;
BENCHMARK(reserveBenchmark, iters) {
while (--iters) {
unique_ptr<IOBuf> iobuf1(IOBuf::create(buf_size));
iobuf1->append(buf_size);
for (size_t bufs = num_bufs; bufs > 1; bufs --) {
iobuf1->reserve(0, buf_size);
iobuf1->append(buf_size);
}
}
}
BENCHMARK(chainBenchmark, iters) {
while (--iters) {
unique_ptr<IOBuf> iobuf1(IOBuf::create(buf_size));
iobuf1->append(buf_size);
for (size_t bufs = num_bufs; bufs > 1; bufs --) {
unique_ptr<IOBuf> iobufNext(IOBuf::create(buf_size));
iobuf1->prependChain(std::move(iobufNext));
}
}
}
vector<unique_ptr<IOBuf>> bufPool;
inline unique_ptr<IOBuf> poolGetIOBuf() {
if (bufPool.size() > 0) {
unique_ptr<IOBuf> ret = std::move(bufPool.back());
bufPool.pop_back();
return std::move(ret);
} else {
unique_ptr<IOBuf> iobuf(IOBuf::create(buf_size));
iobuf->append(buf_size);
return std::move(iobuf);
}
}
inline void poolPutIOBuf(unique_ptr<IOBuf>&& buf) {
unique_ptr<IOBuf> head = std::move(buf);
while (head) {
unique_ptr<IOBuf> next = std::move(head->pop());
bufPool.push_back(std::move(head));
head = std::move(next);
}
}
BENCHMARK(poolBenchmark, iters) {
while (--iters) {
unique_ptr<IOBuf> head = std::move(poolGetIOBuf());
for (size_t bufs = num_bufs; bufs > 1; bufs --) {
unique_ptr<IOBuf> iobufNext = std::move(poolGetIOBuf());
head->prependChain(std::move(iobufNext));
}
// cleanup
poolPutIOBuf(std::move(head));
}
}
void setNumbers(size_t size, size_t num) {
buf_size = size;
num_bufs = num;
bufPool.clear();
printf("\nBuffer size: %zu, number of buffers: %zu\n\n", size, num);
}
/*
------------------------------------------------------------------------------
reserveBenchmark 100000 9.186 ms 91.86 ns 10.38 M
chainBenchmark 100000 59.44 ms 594.4 ns 1.604 M
poolBenchmark 100000 15.87 ms 158.7 ns 6.01 M
Buffer size: 100, number of buffers: 10
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 100000 62 ms 620 ns 1.538 M
chainBenchmark 100000 59.48 ms 594.8 ns 1.603 M
poolBenchmark 100000 16.07 ms 160.7 ns 5.933 M
Buffer size: 2048, number of buffers: 10
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 100000 148.4 ms 1.484 us 658.2 k
chainBenchmark 100000 140.9 ms 1.409 us 693 k
poolBenchmark 100000 16.73 ms 167.3 ns 5.7 M
Buffer size: 10000, number of buffers: 10
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 100000 234 ms 2.34 us 417.3 k
chainBenchmark 100000 142.3 ms 1.423 us 686.1 k
poolBenchmark 100000 16.78 ms 167.8 ns 5.684 M
Buffer size: 100000, number of buffers: 10
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 100000 186.5 ms 1.865 us 523.5 k
chainBenchmark 100000 360.5 ms 3.605 us 270.9 k
poolBenchmark 100000 16.52 ms 165.2 ns 5.772 M
Buffer size: 1000000, number of buffers: 10
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 156 2.084 s 13.36 ms 74.84
chainBenchmark 30082 2.001 s 66.5 us 14.68 k
poolBenchmark 100000 18.18 ms 181.8 ns 5.244 M
Buffer size: 10, number of buffers: 20
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
reserveBenchmark 100000 12.54 ms 125.4 ns 7.603 M
chainBenchmark 100000 118.6 ms 1.186 us 823.2 k
poolBenchmark 100000 32.2 ms 322 ns 2.962 M
*/
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
setNumbers(10, 10);
folly::runBenchmarks();
setNumbers(100, 10);
folly::runBenchmarks();
setNumbers(2048, 10);
folly::runBenchmarks();
setNumbers(10000, 10);
folly::runBenchmarks();
setNumbers(100000, 10);
folly::runBenchmarks();
setNumbers(1000000, 10);
folly::runBenchmarks();
setNumbers(10, 20);
folly::runBenchmarks();
return 0;
}
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