/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
-#include <assert.h>
+#include <cassert>
#include <cstdarg>
+#include <cstring>
+#include <memory>
#include <stdexcept>
-#include <string.h>
#include <type_traits>
-#include <memory>
-#include <folly/Bits.h>
#include <folly/Likely.h>
#include <folly/Memory.h>
#include <folly/Portability.h>
#include <folly/Range.h>
#include <folly/io/IOBuf.h>
#include <folly/io/IOBufQueue.h>
+#include <folly/lang/Bits.h>
#include <folly/portability/BitsFunctexcept.h>
/**
*
* RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
* RWUnshareCursor - Read-write access, calls unshare on write (COW)
- * Appender - Write access, assumes private access to IOBuf chian
+ * Appender - Write access, assumes private access to IOBuf chain
*
* Note that RW cursors write in the preallocated part of buffers (that is,
* between the buffer's data() and tail()), while Appenders append to the end
* Appender with a buffer chain; for this reason, Appenders assume private
* access to the buffer (you need to call unshare() yourself if necessary).
**/
-namespace folly { namespace io {
+namespace folly {
+namespace io {
namespace detail {
template <class T>
T read() {
- T val;
+ T val{};
if (!tryRead(val)) {
std::__throw_out_of_range("underflow");
}
size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
if (!buf) {
- buf = make_unique<folly::IOBuf>();
+ buf = std::make_unique<folly::IOBuf>();
}
return cloneAtMost(*buf, len);
}
BufType* buffer_;
};
-} // namespace detail
+} // namespace detail
class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
public:
using detail::Writable<RWCursor<access>>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
+ // We have to explicitly check for an input length of 0.
+ // We support buf being nullptr in this case, but we need to avoid calling
+ // memcpy() with a null source pointer, since that is undefined behavior
+ // even if the length is 0.
+ if (len == 0) {
+ return 0;
+ }
+
size_t copied = 0;
for (;;) {
// Fast path: the current buffer is big enough.
using detail::Writable<Appender>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
+ // We have to explicitly check for an input length of 0.
+ // We support buf being nullptr in this case, but we need to avoid calling
+ // memcpy() with a null source pointer, since that is undefined behavior
+ // even if the length is 0.
+ if (len == 0) {
+ return 0;
+ }
+
size_t copied = 0;
for (;;) {
// Fast path: it all fits in one buffer.
* space in the queue, we grow no more than growth bytes at once
* (unless you call ensure() with a bigger value yourself).
*/
- QueueAppender(IOBufQueue* queue, uint64_t growth) {
- reset(queue, growth);
- }
+ QueueAppender(IOBufQueue* queue, uint64_t growth)
+ : queueCache_(queue), growth_(growth) {}
void reset(IOBufQueue* queue, uint64_t growth) {
- queue_ = queue;
+ queueCache_.reset(queue);
growth_ = growth;
}
uint8_t* writableData() {
- return static_cast<uint8_t*>(queue_->writableTail());
+ return queueCache_.writableData();
}
- size_t length() const { return queue_->tailroom(); }
+ size_t length() {
+ return queueCache_.length();
+ }
- void append(size_t n) { queue_->postallocate(n); }
+ void append(size_t n) {
+ queueCache_.append(n);
+ }
// Ensure at least n contiguous; can go above growth_, throws if
// not enough room.
- void ensure(uint64_t n) { queue_->preallocate(n, growth_); }
+ void ensure(size_t n) {
+ if (length() < n) {
+ ensureSlow(n);
+ }
+ }
template <class T>
- typename std::enable_if<std::is_arithmetic<T>::value>::type
- write(T value) {
+ typename std::enable_if<std::is_arithmetic<T>::value>::type write(T value) {
// We can't fail.
- auto p = queue_->preallocate(sizeof(T), growth_);
- storeUnaligned(p.first, value);
- queue_->postallocate(sizeof(T));
+ if (length() >= sizeof(T)) {
+ storeUnaligned(queueCache_.writableData(), value);
+ queueCache_.appendUnsafe(sizeof(T));
+ } else {
+ writeSlow<T>(value);
+ }
}
using detail::Writable<QueueAppender>::pushAtMost;
const size_t copyLength = std::min(len, length());
if (copyLength != 0) {
memcpy(writableData(), buf, copyLength);
- append(copyLength);
+ queueCache_.appendUnsafe(copyLength);
buf += copyLength;
}
- // Allocate more buffers as necessary
size_t remaining = len - copyLength;
+ // Allocate more buffers as necessary
while (remaining != 0) {
- auto p = queue_->preallocate(std::min(remaining, growth_),
- growth_,
- remaining);
+ auto p = queueCache_.queue()->preallocate(
+ std::min(remaining, growth_), growth_, remaining);
memcpy(p.first, buf, p.second);
- queue_->postallocate(p.second);
+ queueCache_.queue()->postallocate(p.second);
buf += p.second;
remaining -= p.second;
}
-
return len;
}
void insert(std::unique_ptr<folly::IOBuf> buf) {
if (buf) {
- queue_->append(std::move(buf), true);
+ queueCache_.queue()->append(std::move(buf), true);
}
}
}
private:
- folly::IOBufQueue* queue_;
- size_t growth_;
+ folly::IOBufQueue::WritableRangeCache queueCache_{nullptr};
+ size_t growth_{0};
+
+ FOLLY_NOINLINE void ensureSlow(size_t n) {
+ queueCache_.queue()->preallocate(n, growth_);
+ queueCache_.fillCache();
+ }
+
+ template <class T>
+ typename std::enable_if<std::is_arithmetic<T>::value>::type FOLLY_NOINLINE
+ writeSlow(T value) {
+ queueCache_.queue()->preallocate(sizeof(T), growth_);
+ queueCache_.fillCache();
+
+ storeUnaligned(queueCache_.writableData(), value);
+ queueCache_.appendUnsafe(sizeof(T));
+ }
};
-}} // folly::io
+} // namespace io
+} // namespace folly
#include <folly/io/Cursor-inl.h>