2 * Copyright 2013 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/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 clone(std::unique_ptr<folly::IOBuf>& buf, size_t length) {
131 if (UNLIKELY(cloneAtMost(buf, length) != length)) {
132 throw std::out_of_range("underflow");
136 void skip(size_t length) {
137 if (UNLIKELY(skipAtMost(length) != length)) {
138 throw std::out_of_range("underflow");
142 size_t pullAtMost(void* buf, size_t len) {
143 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
146 // Fast path: it all fits in one buffer.
147 size_t available = length();
148 if (LIKELY(available >= len)) {
149 memcpy(p, data(), len);
154 memcpy(p, data(), available);
156 if (UNLIKELY(!tryAdvanceBuffer())) {
164 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
167 std::unique_ptr<folly::IOBuf> tmp;
170 // Fast path: it all fits in one buffer.
171 size_t available = length();
172 if (LIKELY(available >= len)) {
173 tmp = crtBuf_->cloneOne();
174 tmp->trimStart(offset_);
175 tmp->trimEnd(tmp->length() - len);
178 buf = std::move(tmp);
180 buf->prependChain(std::move(tmp));
185 tmp = crtBuf_->cloneOne();
186 tmp->trimStart(offset_);
188 buf = std::move(tmp);
190 buf->prependChain(std::move(tmp));
194 if (UNLIKELY(!tryAdvanceBuffer())) {
201 size_t skipAtMost(size_t len) {
204 // Fast path: it all fits in one buffer.
205 size_t available = length();
206 if (LIKELY(available >= len)) {
208 return skipped + len;
211 skipped += available;
212 if (UNLIKELY(!tryAdvanceBuffer())) {
220 * Return the distance between two cursors.
222 size_t operator-(const CursorBase& other) const {
223 BufType *otherBuf = other.crtBuf_;
226 if (otherBuf != crtBuf_) {
227 len += otherBuf->length() - other.offset_;
229 for (otherBuf = otherBuf->next();
230 otherBuf != crtBuf_ && otherBuf != other.buffer_;
231 otherBuf = otherBuf->next()) {
232 len += otherBuf->length();
235 if (otherBuf == other.buffer_) {
236 throw std::out_of_range("wrap-around");
241 if (offset_ < other.offset_) {
242 throw std::out_of_range("underflow");
245 len += offset_ - other.offset_;
252 * Return the distance from the given IOBuf to the this cursor.
254 size_t operator-(const BufType* buf) const {
257 BufType *curBuf = buf;
258 while (curBuf != crtBuf_) {
259 len += curBuf->length();
260 curBuf = curBuf->next();
261 if (curBuf == buf || curBuf == buffer_) {
262 throw std::out_of_range("wrap-around");
276 bool tryAdvanceBuffer() {
277 BufType* nextBuf = crtBuf_->next();
278 if (UNLIKELY(nextBuf == buffer_)) {
279 offset_ = crtBuf_->length();
285 static_cast<Derived*>(this)->advanceDone();
296 template <class Derived>
300 typename std::enable_if<std::is_integral<T>::value>::type
302 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
307 void writeBE(T value) {
308 write(Endian::big(value));
312 void writeLE(T value) {
313 write(Endian::little(value));
316 void push(const uint8_t* buf, size_t len) {
317 Derived* d = static_cast<Derived*>(this);
318 if (d->pushAtMost(buf, len) != len) {
319 throw std::out_of_range("overflow");
324 } // namespace detail
326 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
328 explicit Cursor(const IOBuf* buf)
329 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
331 template <class CursorType>
332 explicit Cursor(CursorType& cursor)
333 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
336 enum class CursorAccess {
341 template <CursorAccess access>
343 : public detail::CursorBase<RWCursor<access>, IOBuf>,
344 public detail::Writable<RWCursor<access>> {
345 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
347 explicit RWCursor(IOBuf* buf)
348 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
349 maybeShared_(true) {}
351 template <class CursorType>
352 explicit RWCursor(CursorType& cursor)
353 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
354 maybeShared_(true) {}
356 * Gather at least n bytes contiguously into the current buffer,
357 * by coalescing subsequent buffers from the chain as necessary.
359 void gather(size_t n) {
360 this->crtBuf_->gather(this->offset_ + n);
363 size_t pushAtMost(const uint8_t* buf, size_t len) {
366 // Fast path: the current buffer is big enough.
367 size_t available = this->length();
368 if (LIKELY(available >= len)) {
369 if (access == CursorAccess::UNSHARE) {
372 memcpy(writableData(), buf, len);
373 this->offset_ += len;
377 if (access == CursorAccess::UNSHARE) {
380 memcpy(writableData(), buf, available);
382 if (UNLIKELY(!this->tryAdvanceBuffer())) {
390 void insert(std::unique_ptr<folly::IOBuf> buf) {
391 folly::IOBuf* nextBuf;
392 if (this->offset_ == 0) {
395 this->crtBuf_->prependChain(std::move(buf));
397 std::unique_ptr<folly::IOBuf> remaining;
398 if (this->crtBuf_->length() - this->offset_ > 0) {
399 // Need to split current IOBuf in two.
400 remaining = this->crtBuf_->cloneOne();
401 remaining->trimStart(this->offset_);
402 nextBuf = remaining.get();
403 buf->prependChain(std::move(remaining));
406 nextBuf = this->crtBuf_->next();
408 this->crtBuf_->trimEnd(this->length());
409 this->crtBuf_->appendChain(std::move(buf));
411 // Jump past the new links
413 this->crtBuf_ = nextBuf;
416 uint8_t* writableData() {
417 return this->crtBuf_->writableData() + this->offset_;
421 void maybeUnshare() {
422 if (UNLIKELY(maybeShared_)) {
423 this->crtBuf_->unshareOne();
424 maybeShared_ = false;
435 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
436 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
439 * Append to the end of a buffer chain, growing the chain (by allocating new
440 * buffers) in increments of at least growth bytes every time. Won't grow
441 * (and push() and ensure() will throw) if growth == 0.
443 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
446 class Appender : public detail::Writable<Appender> {
448 Appender(IOBuf* buf, uint32_t growth)
450 crtBuf_(buf->prev()),
454 uint8_t* writableData() {
455 return crtBuf_->writableTail();
458 size_t length() const {
459 return crtBuf_->tailroom();
463 * Mark n bytes (must be <= length()) as appended, as per the
464 * IOBuf::append() method.
466 void append(size_t n) {
471 * Ensure at least n contiguous bytes available to write.
472 * Postcondition: length() >= n.
474 void ensure(uint32_t n) {
475 if (LIKELY(length() >= n)) {
479 // Waste the rest of the current buffer and allocate a new one.
480 // Don't make it too small, either.
482 throw std::out_of_range("can't grow buffer chain");
485 n = std::max(n, growth_);
486 buffer_->prependChain(IOBuf::create(n));
487 crtBuf_ = buffer_->prev();
490 size_t pushAtMost(const uint8_t* buf, size_t len) {
493 // Fast path: it all fits in one buffer.
494 size_t available = length();
495 if (LIKELY(available >= len)) {
496 memcpy(writableData(), buf, len);
501 memcpy(writableData(), buf, available);
504 if (UNLIKELY(!tryGrowChain())) {
513 bool tryGrowChain() {
514 assert(crtBuf_->next() == buffer_);
519 buffer_->prependChain(IOBuf::create(growth_));
520 crtBuf_ = buffer_->prev();
531 #endif // FOLLY_CURSOR_H