/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 {
return true;
}
+ /**
+ * Advances the cursor to the end of the entire IOBuf chain.
+ */
+ void advanceToEnd() {
+ offset_ = buffer_->prev()->length();
+ if (crtBuf_ != buffer_->prev()) {
+ crtBuf_ = buffer_->prev();
+ static_cast<Derived*>(this)->advanceDone();
+ }
+ }
+
Derived& operator+=(size_t offset) {
Derived* p = static_cast<Derived*>(this);
p->skip(offset);
return other;
}
+ Derived& operator-=(size_t offset) {
+ Derived* p = static_cast<Derived*>(this);
+ p->retreat(offset);
+ return *p;
+ }
+ Derived operator-(size_t offset) const {
+ Derived other(*this);
+ other.retreat(offset);
+ return other;
+ }
+
/**
* Compare cursors for equality/inequality.
*
}
template <class T>
- typename std::enable_if<std::is_arithmetic<T>::value, T>::type read() {
- T val;
+ typename std::enable_if<std::is_arithmetic<T>::value, bool>::type tryRead(
+ T& val) {
if (LIKELY(length() >= sizeof(T))) {
val = loadUnaligned<T>(data());
offset_ += sizeof(T);
advanceBufferIfEmpty();
- } else {
- pullSlow(&val, sizeof(T));
+ return true;
+ }
+ return pullAtMostSlow(&val, sizeof(T)) == sizeof(T);
+ }
+
+ template <class T>
+ bool tryReadBE(T& val) {
+ const bool result = tryRead(val);
+ val = Endian::big(val);
+ return result;
+ }
+
+ template <class T>
+ bool tryReadLE(T& val) {
+ const bool result = tryRead(val);
+ val = Endian::little(val);
+ return result;
+ }
+
+ template <class T>
+ T read() {
+ T val{};
+ if (!tryRead(val)) {
+ std::__throw_out_of_range("underflow");
}
return val;
}
}
}
+ size_t retreatAtMost(size_t len) {
+ if (len <= offset_) {
+ offset_ -= len;
+ return len;
+ }
+ return retreatAtMostSlow(len);
+ }
+
+ void retreat(size_t len) {
+ if (len <= offset_) {
+ offset_ -= len;
+ } else {
+ retreatSlow(len);
+ }
+ }
+
size_t pullAtMost(void* buf, size_t len) {
// Fast path: it all fits in one buffer.
if (LIKELY(length() >= len)) {
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);
}
return true;
}
+ bool tryRetreatBuffer() {
+ if (UNLIKELY(crtBuf_ == buffer_)) {
+ offset_ = 0;
+ return false;
+ }
+ crtBuf_ = crtBuf_->prev();
+ offset_ = crtBuf_->length();
+ static_cast<Derived*>(this)->advanceDone();
+ return true;
+ }
+
void advanceBufferIfEmpty() {
if (length() == 0) {
tryAdvanceBuffer();
}
}
+ size_t retreatAtMostSlow(size_t len) {
+ size_t retreated = 0;
+ for (size_t available; (available = offset_) < len;) {
+ retreated += available;
+ if (UNLIKELY(!tryRetreatBuffer())) {
+ return retreated;
+ }
+ len -= available;
+ }
+ offset_ -= len;
+ return retreated + len;
+ }
+
+ void retreatSlow(size_t len) {
+ if (UNLIKELY(retreatAtMostSlow(len) != len)) {
+ std::__throw_out_of_range("underflow");
+ }
+ }
+
void advanceDone() {
}
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.
using detail::Writable<QueueAppender>::pushAtMost;
size_t pushAtMost(const uint8_t* buf, size_t len) {
- size_t remaining = len;
+ // Fill the current buffer
+ const size_t copyLength = std::min(len, length());
+ if (copyLength != 0) {
+ memcpy(writableData(), buf, copyLength);
+ append(copyLength);
+ buf += copyLength;
+ }
+ // Allocate more buffers as necessary
+ size_t remaining = len - copyLength;
while (remaining != 0) {
auto p = queue_->preallocate(std::min(remaining, growth_),
growth_,
folly::IOBufQueue* queue_;
size_t growth_;
};
-
-}} // folly::io
+} // namespace io
+} // namespace folly
#include <folly/io/Cursor-inl.h>