2 * Copyright 2014 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/io/IOBufQueue.h"
29 #include "folly/Likely.h"
30 #include "folly/Memory.h"
33 * Cursor class for fast iteration over IOBuf chains.
35 * Cursor - Read-only access
37 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
38 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
39 * Appender - Write access, assumes private access to IOBuf chian
41 * Note that RW cursors write in the preallocated part of buffers (that is,
42 * between the buffer's data() and tail()), while Appenders append to the end
43 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
44 * automatically adjust the buffer pointers, so you may only use one
45 * Appender with a buffer chain; for this reason, Appenders assume private
46 * access to the buffer (you need to call unshare() yourself if necessary).
48 namespace folly { namespace io {
51 template <class Derived, typename BufType>
54 const uint8_t* data() const {
55 return crtBuf_->data() + offset_;
59 * Return the remaining space available in the current IOBuf.
61 * May return 0 if the cursor is at the end of an IOBuf. Use peek() instead
62 * if you want to avoid this. peek() will advance to the next non-empty
63 * IOBuf (up to the end of the chain) if the cursor is currently pointing at
64 * the end of a buffer.
66 size_t length() const {
67 return crtBuf_->length() - offset_;
71 * Return the space available until the end of the entire IOBuf chain.
73 size_t totalLength() const {
74 if (crtBuf_ == buffer_) {
75 return crtBuf_->computeChainDataLength() - offset_;
77 CursorBase end(buffer_->prev());
78 end.offset_ = end.buffer_->length();
82 Derived& operator+=(size_t offset) {
83 Derived* p = static_cast<Derived*>(this);
89 typename std::enable_if<std::is_integral<T>::value, T>::type
92 pull(&val, sizeof(T));
98 return Endian::big(read<T>());
103 return Endian::little(read<T>());
107 * Read a fixed-length string.
109 * The std::string-based APIs should probably be avoided unless you
110 * ultimately want the data to live in an std::string. You're better off
111 * using the pull() APIs to copy into a raw buffer otherwise.
113 std::string readFixedString(size_t len) {
118 // Fast path: it all fits in one buffer.
119 size_t available = length();
120 if (LIKELY(available >= len)) {
121 str.append(reinterpret_cast<const char*>(data()), len);
126 str.append(reinterpret_cast<const char*>(data()), available);
127 if (UNLIKELY(!tryAdvanceBuffer())) {
128 throw std::out_of_range("string underflow");
135 * Read a string consisting of bytes until the given terminator character is
136 * seen. Raises an std::length_error if maxLength bytes have been processed
137 * before the terminator is seen.
139 * See comments in readFixedString() about when it's appropriate to use this
142 std::string readTerminatedString(
143 char termChar = '\0',
144 size_t maxLength = std::numeric_limits<size_t>::max()) {
148 const uint8_t* buf = data();
149 size_t buflen = length();
152 while (i < buflen && buf[i] != termChar) {
155 // Do this check after incrementing 'i', as even though we start at the
156 // 0 byte, it still represents a single character
157 if (str.length() + i >= maxLength) {
158 throw std::length_error("string overflow");
162 str.append(reinterpret_cast<const char*>(buf), i);
170 if (UNLIKELY(!tryAdvanceBuffer())) {
171 throw std::out_of_range("string underflow");
176 explicit CursorBase(BufType* buf)
181 // Make all the templated classes friends for copy constructor.
182 template <class D, typename B> friend class CursorBase;
185 explicit CursorBase(const T& cursor) {
186 crtBuf_ = cursor.crtBuf_;
187 offset_ = cursor.offset_;
188 buffer_ = cursor.buffer_;
191 // reset cursor to point to a new buffer.
192 void reset(BufType* buf) {
199 * Return the available data in the current buffer.
200 * If you want to gather more data from the chain into a contiguous region
201 * (for hopefully zero-copy access), use gather() before peek().
203 std::pair<const uint8_t*, size_t> peek() {
204 // Ensure that we're pointing to valid data
205 size_t available = length();
206 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
207 available = length();
210 return std::make_pair(data(), available);
213 void pull(void* buf, size_t len) {
214 if (UNLIKELY(pullAtMost(buf, len) != len)) {
215 throw std::out_of_range("underflow");
219 void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
220 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
221 throw std::out_of_range("underflow");
225 void clone(folly::IOBuf& buf, size_t len) {
226 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
227 throw std::out_of_range("underflow");
231 void skip(size_t len) {
232 if (UNLIKELY(skipAtMost(len) != len)) {
233 throw std::out_of_range("underflow");
237 size_t pullAtMost(void* buf, size_t len) {
238 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
241 // Fast path: it all fits in one buffer.
242 size_t available = length();
243 if (LIKELY(available >= len)) {
244 memcpy(p, data(), len);
249 memcpy(p, data(), available);
251 if (UNLIKELY(!tryAdvanceBuffer())) {
259 size_t cloneAtMost(folly::IOBuf& buf, size_t len) {
260 buf = folly::IOBuf();
262 std::unique_ptr<folly::IOBuf> tmp;
264 for (int loopCount = 0; true; ++loopCount) {
265 // Fast path: it all fits in one buffer.
266 size_t available = length();
267 if (LIKELY(available >= len)) {
268 if (loopCount == 0) {
269 crtBuf_->cloneOneInto(buf);
270 buf.trimStart(offset_);
271 buf.trimEnd(buf.length() - len);
273 tmp = crtBuf_->cloneOne();
274 tmp->trimStart(offset_);
275 tmp->trimEnd(tmp->length() - len);
276 buf.prependChain(std::move(tmp));
284 if (loopCount == 0) {
285 crtBuf_->cloneOneInto(buf);
286 buf.trimStart(offset_);
288 tmp = crtBuf_->cloneOne();
289 tmp->trimStart(offset_);
290 buf.prependChain(std::move(tmp));
294 if (UNLIKELY(!tryAdvanceBuffer())) {
301 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
303 buf = make_unique<folly::IOBuf>();
306 return cloneAtMost(*buf, len);
309 size_t skipAtMost(size_t len) {
312 // Fast path: it all fits in one buffer.
313 size_t available = length();
314 if (LIKELY(available >= len)) {
316 return skipped + len;
319 skipped += available;
320 if (UNLIKELY(!tryAdvanceBuffer())) {
328 * Return the distance between two cursors.
330 size_t operator-(const CursorBase& other) const {
331 BufType *otherBuf = other.crtBuf_;
334 if (otherBuf != crtBuf_) {
335 len += otherBuf->length() - other.offset_;
337 for (otherBuf = otherBuf->next();
338 otherBuf != crtBuf_ && otherBuf != other.buffer_;
339 otherBuf = otherBuf->next()) {
340 len += otherBuf->length();
343 if (otherBuf == other.buffer_) {
344 throw std::out_of_range("wrap-around");
349 if (offset_ < other.offset_) {
350 throw std::out_of_range("underflow");
353 len += offset_ - other.offset_;
360 * Return the distance from the given IOBuf to the this cursor.
362 size_t operator-(const BufType* buf) const {
365 BufType *curBuf = buf;
366 while (curBuf != crtBuf_) {
367 len += curBuf->length();
368 curBuf = curBuf->next();
369 if (curBuf == buf || curBuf == buffer_) {
370 throw std::out_of_range("wrap-around");
388 bool tryAdvanceBuffer() {
389 BufType* nextBuf = crtBuf_->next();
390 if (UNLIKELY(nextBuf == buffer_)) {
391 offset_ = crtBuf_->length();
397 static_cast<Derived*>(this)->advanceDone();
408 template <class Derived>
412 typename std::enable_if<std::is_integral<T>::value>::type
414 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
415 Derived* d = static_cast<Derived*>(this);
416 d->push(u8, sizeof(T));
420 void writeBE(T value) {
421 Derived* d = static_cast<Derived*>(this);
422 d->write(Endian::big(value));
426 void writeLE(T value) {
427 Derived* d = static_cast<Derived*>(this);
428 d->write(Endian::little(value));
431 void push(const uint8_t* buf, size_t len) {
432 Derived* d = static_cast<Derived*>(this);
433 if (d->pushAtMost(buf, len) != len) {
434 throw std::out_of_range("overflow");
439 } // namespace detail
441 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
443 explicit Cursor(const IOBuf* buf)
444 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
446 template <class CursorType>
447 explicit Cursor(CursorType& cursor)
448 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
451 enum class CursorAccess {
456 template <CursorAccess access>
458 : public detail::CursorBase<RWCursor<access>, IOBuf>,
459 public detail::Writable<RWCursor<access>> {
460 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
462 explicit RWCursor(IOBuf* buf)
463 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
464 maybeShared_(true) {}
466 template <class CursorType>
467 explicit RWCursor(CursorType& cursor)
468 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
469 maybeShared_(true) {}
471 * Gather at least n bytes contiguously into the current buffer,
472 * by coalescing subsequent buffers from the chain as necessary.
474 void gather(size_t n) {
475 // Forbid attempts to gather beyond the end of this IOBuf chain.
476 // Otherwise we could try to coalesce the head of the chain and end up
477 // accidentally freeing it, invalidating the pointer owned by external
480 // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
481 // checking. We only have to perform an explicit check here when calling
482 // gather() on a non-head element.
483 if (this->crtBuf_ != this->head() && this->totalLength() < n) {
484 throw std::overflow_error("cannot gather() past the end of the chain");
486 this->crtBuf_->gather(this->offset_ + n);
488 void gatherAtMost(size_t n) {
489 size_t size = std::min(n, this->totalLength());
490 return this->crtBuf_->gather(this->offset_ + size);
493 size_t pushAtMost(const uint8_t* buf, size_t len) {
496 // Fast path: the current buffer is big enough.
497 size_t available = this->length();
498 if (LIKELY(available >= len)) {
499 if (access == CursorAccess::UNSHARE) {
502 memcpy(writableData(), buf, len);
503 this->offset_ += len;
507 if (access == CursorAccess::UNSHARE) {
510 memcpy(writableData(), buf, available);
512 if (UNLIKELY(!this->tryAdvanceBuffer())) {
520 void insert(std::unique_ptr<folly::IOBuf> buf) {
521 folly::IOBuf* nextBuf;
522 if (this->offset_ == 0) {
524 nextBuf = this->crtBuf_;
525 this->crtBuf_->prependChain(std::move(buf));
527 std::unique_ptr<folly::IOBuf> remaining;
528 if (this->crtBuf_->length() - this->offset_ > 0) {
529 // Need to split current IOBuf in two.
530 remaining = this->crtBuf_->cloneOne();
531 remaining->trimStart(this->offset_);
532 nextBuf = remaining.get();
533 buf->prependChain(std::move(remaining));
536 nextBuf = this->crtBuf_->next();
538 this->crtBuf_->trimEnd(this->length());
539 this->crtBuf_->appendChain(std::move(buf));
541 // Jump past the new links
543 this->crtBuf_ = nextBuf;
546 uint8_t* writableData() {
547 return this->crtBuf_->writableData() + this->offset_;
551 void maybeUnshare() {
552 if (UNLIKELY(maybeShared_)) {
553 this->crtBuf_->unshareOne();
554 maybeShared_ = false;
565 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
566 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
569 * Append to the end of a buffer chain, growing the chain (by allocating new
570 * buffers) in increments of at least growth bytes every time. Won't grow
571 * (and push() and ensure() will throw) if growth == 0.
573 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
576 class Appender : public detail::Writable<Appender> {
578 Appender(IOBuf* buf, uint32_t growth)
580 crtBuf_(buf->prev()),
584 uint8_t* writableData() {
585 return crtBuf_->writableTail();
588 size_t length() const {
589 return crtBuf_->tailroom();
593 * Mark n bytes (must be <= length()) as appended, as per the
594 * IOBuf::append() method.
596 void append(size_t n) {
601 * Ensure at least n contiguous bytes available to write.
602 * Postcondition: length() >= n.
604 void ensure(uint32_t n) {
605 if (LIKELY(length() >= n)) {
609 // Waste the rest of the current buffer and allocate a new one.
610 // Don't make it too small, either.
612 throw std::out_of_range("can't grow buffer chain");
615 n = std::max(n, growth_);
616 buffer_->prependChain(IOBuf::create(n));
617 crtBuf_ = buffer_->prev();
620 size_t pushAtMost(const uint8_t* buf, size_t len) {
623 // Fast path: it all fits in one buffer.
624 size_t available = length();
625 if (LIKELY(available >= len)) {
626 memcpy(writableData(), buf, len);
631 memcpy(writableData(), buf, available);
634 if (UNLIKELY(!tryGrowChain())) {
643 bool tryGrowChain() {
644 assert(crtBuf_->next() == buffer_);
649 buffer_->prependChain(IOBuf::create(growth_));
650 crtBuf_ = buffer_->prev();
659 class QueueAppender : public detail::Writable<QueueAppender> {
662 * Create an Appender that writes to a IOBufQueue. When we allocate
663 * space in the queue, we grow no more than growth bytes at once
664 * (unless you call ensure() with a bigger value yourself).
666 QueueAppender(IOBufQueue* queue, uint32_t growth) {
667 reset(queue, growth);
670 void reset(IOBufQueue* queue, uint32_t growth) {
675 uint8_t* writableData() {
676 return static_cast<uint8_t*>(queue_->writableTail());
679 size_t length() const { return queue_->tailroom(); }
681 void append(size_t n) { queue_->postallocate(n); }
683 // Ensure at least n contiguous; can go above growth_, throws if
685 void ensure(uint32_t n) { queue_->preallocate(n, growth_); }
688 typename std::enable_if<std::is_integral<T>::value>::type
691 auto p = queue_->preallocate(sizeof(T), growth_);
692 storeUnaligned(p.first, value);
693 queue_->postallocate(sizeof(T));
697 size_t pushAtMost(const uint8_t* buf, size_t len) {
698 size_t remaining = len;
699 while (remaining != 0) {
700 auto p = queue_->preallocate(std::min(remaining, growth_),
703 memcpy(p.first, buf, p.second);
704 queue_->postallocate(p.second);
706 remaining -= p.second;
712 void insert(std::unique_ptr<folly::IOBuf> buf) {
714 queue_->append(std::move(buf), true);
719 folly::IOBufQueue* queue_;
725 #endif // FOLLY_CURSOR_H