2 * Copyright 2012 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #ifndef FOLLY_CURSOR_H
18 #define FOLLY_CURSOR_H
23 #include <type_traits>
26 #include "folly/Bits.h"
27 #include "folly/experimental/io/IOBuf.h"
28 #include "folly/Likely.h"
31 * Cursor class for fast iteration over IOBuf chains.
33 * Cursor - Read-only access
35 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
36 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
37 * Appender - Write access, assumes private access to IOBuf chian
39 * Note that RW cursors write in the preallocated part of buffers (that is,
40 * between the buffer's data() and tail()), while Appenders append to the end
41 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
42 * automatically adjust the buffer pointers, so you may only use one
43 * Appender with a buffer chain; for this reason, Appenders assume private
44 * access to the buffer (you need to call unshare() yourself if necessary).
46 namespace folly { namespace io {
49 template <class Derived, typename BufType>
52 const uint8_t* data() const {
53 return crtBuf_->data() + offset_;
56 // Space available in the current IOBuf. May be 0; use peek() instead which
57 // will always point to a non-empty chunk of data or at the end of the
59 size_t length() const {
60 return crtBuf_->length() - offset_;
63 Derived& operator+=(size_t offset) {
64 Derived* p = static_cast<Derived*>(this);
70 typename std::enable_if<std::is_integral<T>::value, T>::type
73 pull(&val, sizeof(T));
79 return Endian::big(read<T>());
84 return Endian::little(read<T>());
87 explicit CursorBase(BufType* buf)
92 // Make all the templated classes friends for copy constructor.
93 template <class D, typename B> friend class CursorBase;
96 explicit CursorBase(const T& cursor) {
97 crtBuf_ = cursor.crtBuf_;
98 offset_ = cursor.offset_;
99 buffer_ = cursor.buffer_;
102 // reset cursor to point to a new buffer.
103 void reset(BufType* buf) {
110 * Return the available data in the current buffer.
111 * If you want to gather more data from the chain into a contiguous region
112 * (for hopefully zero-copy access), use gather() before peek().
114 std::pair<const uint8_t*, size_t> peek() {
115 // Ensure that we're pointing to valid data
116 size_t available = length();
117 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
118 available = length();
121 return std::make_pair(data(), available);
124 void pull(void* buf, size_t length) {
125 if (UNLIKELY(pullAtMost(buf, length) != length)) {
126 throw std::out_of_range("underflow");
130 void skip(size_t length) {
131 if (UNLIKELY(skipAtMost(length) != length)) {
132 throw std::out_of_range("underflow");
136 size_t pullAtMost(void* buf, size_t len) {
137 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
140 // Fast path: it all fits in one buffer.
141 size_t available = length();
142 if (LIKELY(available >= len)) {
143 memcpy(p, data(), len);
148 memcpy(p, data(), available);
150 if (UNLIKELY(!tryAdvanceBuffer())) {
158 size_t skipAtMost(size_t len) {
161 // Fast path: it all fits in one buffer.
162 size_t available = length();
163 if (LIKELY(available >= len)) {
165 return skipped + len;
168 skipped += available;
169 if (UNLIKELY(!tryAdvanceBuffer())) {
182 bool tryAdvanceBuffer() {
183 BufType* nextBuf = crtBuf_->next();
184 if (UNLIKELY(nextBuf == buffer_)) {
185 offset_ = crtBuf_->length();
191 static_cast<Derived*>(this)->advanceDone();
202 template <class Derived>
206 typename std::enable_if<std::is_integral<T>::value>::type
208 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
213 void writeBE(T value) {
214 write(Endian::big(value));
218 void writeLE(T value) {
219 write(Endian::little(value));
222 void push(const uint8_t* buf, size_t len) {
223 Derived* d = static_cast<Derived*>(this);
224 if (d->pushAtMost(buf, len) != len) {
225 throw std::out_of_range("overflow");
230 } // namespace detail
232 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
234 explicit Cursor(const IOBuf* buf)
235 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
237 template <class CursorType>
238 explicit Cursor(CursorType& cursor)
239 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
242 enum class CursorAccess {
247 template <CursorAccess access>
249 : public detail::CursorBase<RWCursor<access>, IOBuf>,
250 public detail::Writable<RWCursor<access>> {
251 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
253 explicit RWCursor(IOBuf* buf)
254 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
255 maybeShared_(true) {}
257 template <class CursorType>
258 explicit RWCursor(CursorType& cursor)
259 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
260 maybeShared_(true) {}
262 * Gather at least n bytes contiguously into the current buffer,
263 * by coalescing subsequent buffers from the chain as necessary.
265 void gather(size_t n) {
266 this->crtBuf_->gather(this->offset_ + n);
269 size_t pushAtMost(const uint8_t* buf, size_t len) {
272 // Fast path: the current buffer is big enough.
273 size_t available = this->length();
274 if (LIKELY(available >= len)) {
275 if (access == CursorAccess::UNSHARE) {
278 memcpy(writableData(), buf, len);
279 this->offset_ += len;
283 if (access == CursorAccess::UNSHARE) {
286 memcpy(writableData(), buf, available);
288 if (UNLIKELY(!this->tryAdvanceBuffer())) {
296 uint8_t* writableData() {
297 return this->crtBuf_->writableData() + this->offset_;
301 void maybeUnshare() {
302 if (UNLIKELY(maybeShared_)) {
303 this->crtBuf_->unshareOne();
304 maybeShared_ = false;
315 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
316 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
319 * Append to the end of a buffer chain, growing the chain (by allocating new
320 * buffers) in increments of at least growth bytes every time. Won't grow
321 * (and push() and ensure() will throw) if growth == 0.
323 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
326 class Appender : public detail::Writable<Appender> {
328 Appender(IOBuf* buf, uint32_t growth)
330 crtBuf_(buf->prev()),
334 uint8_t* writableData() {
335 return crtBuf_->writableTail();
338 size_t length() const {
339 return crtBuf_->tailroom();
343 * Mark n bytes (must be <= length()) as appended, as per the
344 * IOBuf::append() method.
346 void append(size_t n) {
351 * Ensure at least n contiguous bytes available to write.
352 * Postcondition: length() >= n.
354 void ensure(uint32_t n) {
355 if (LIKELY(length() >= n)) {
359 // Waste the rest of the current buffer and allocate a new one.
360 // Don't make it too small, either.
362 throw std::out_of_range("can't grow buffer chain");
365 n = std::max(n, growth_);
366 buffer_->prependChain(IOBuf::create(n));
367 crtBuf_ = buffer_->prev();
370 size_t pushAtMost(const uint8_t* buf, size_t len) {
373 // Fast path: it all fits in one buffer.
374 size_t available = length();
375 if (LIKELY(available >= len)) {
376 memcpy(writableData(), buf, len);
381 memcpy(writableData(), buf, available);
384 if (UNLIKELY(!tryGrowChain())) {
393 bool tryGrowChain() {
394 assert(crtBuf_->next() == buffer_);
399 buffer_->prependChain(IOBuf::create(growth_));
400 crtBuf_ = buffer_->prev();
411 #endif // FOLLY_CURSOR_H