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/io/IOBufQueue.h"
29 #include "folly/Likely.h"
32 * Cursor class for fast iteration over IOBuf chains.
34 * Cursor - Read-only access
36 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
37 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
38 * Appender - Write access, assumes private access to IOBuf chian
40 * Note that RW cursors write in the preallocated part of buffers (that is,
41 * between the buffer's data() and tail()), while Appenders append to the end
42 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
43 * automatically adjust the buffer pointers, so you may only use one
44 * Appender with a buffer chain; for this reason, Appenders assume private
45 * access to the buffer (you need to call unshare() yourself if necessary).
47 namespace folly { namespace io {
50 template <class Derived, typename BufType>
53 const uint8_t* data() const {
54 return crtBuf_->data() + offset_;
58 * Return the remaining space available in the current IOBuf.
60 * May return 0 if the cursor is at the end of an IOBuf. Use peek() instead
61 * if you want to avoid this. peek() will advance to the next non-empty
62 * IOBuf (up to the end of the chain) if the cursor is currently pointing at
63 * the end of a buffer.
65 size_t length() const {
66 return crtBuf_->length() - offset_;
70 * Return the space available until the end of the entire IOBuf chain.
72 size_t totalLength() const {
73 if (crtBuf_ == buffer_) {
74 return crtBuf_->computeChainDataLength() - offset_;
76 CursorBase end(buffer_->prev());
77 end.offset_ = end.buffer_->length();
81 Derived& operator+=(size_t offset) {
82 Derived* p = static_cast<Derived*>(this);
88 typename std::enable_if<std::is_integral<T>::value, T>::type
91 pull(&val, sizeof(T));
97 return Endian::big(read<T>());
102 return Endian::little(read<T>());
106 * Read a fixed-length string.
108 * The std::string-based APIs should probably be avoided unless you
109 * ultimately want the data to live in an std::string. You're better off
110 * using the pull() APIs to copy into a raw buffer otherwise.
112 std::string readFixedString(size_t len) {
117 // Fast path: it all fits in one buffer.
118 size_t available = length();
119 if (LIKELY(available >= len)) {
120 str.append(reinterpret_cast<const char*>(data()), len);
125 str.append(reinterpret_cast<const char*>(data()), available);
126 if (UNLIKELY(!tryAdvanceBuffer())) {
127 throw std::out_of_range("string underflow");
134 * Read a string consisting of bytes until the given terminator character is
135 * seen. Raises an std::length_error if maxLength bytes have been processed
136 * before the terminator is seen.
138 * See comments in readFixedString() about when it's appropriate to use this
141 std::string readTerminatedString(
142 char termChar = '\0',
143 size_t maxLength = std::numeric_limits<size_t>::max()) {
147 const uint8_t* buf = data();
148 size_t buflen = length();
151 while (i < buflen && buf[i] != termChar) {
154 // Do this check after incrementing 'i', as even though we start at the
155 // 0 byte, it still represents a single character
156 if (str.length() + i >= maxLength) {
157 throw std::length_error("string overflow");
161 str.append(reinterpret_cast<const char*>(buf), i);
169 if (UNLIKELY(!tryAdvanceBuffer())) {
170 throw std::out_of_range("string underflow");
175 explicit CursorBase(BufType* buf)
180 // Make all the templated classes friends for copy constructor.
181 template <class D, typename B> friend class CursorBase;
184 explicit CursorBase(const T& cursor) {
185 crtBuf_ = cursor.crtBuf_;
186 offset_ = cursor.offset_;
187 buffer_ = cursor.buffer_;
190 // reset cursor to point to a new buffer.
191 void reset(BufType* buf) {
198 * Return the available data in the current buffer.
199 * If you want to gather more data from the chain into a contiguous region
200 * (for hopefully zero-copy access), use gather() before peek().
202 std::pair<const uint8_t*, size_t> peek() {
203 // Ensure that we're pointing to valid data
204 size_t available = length();
205 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
206 available = length();
209 return std::make_pair(data(), available);
212 void pull(void* buf, size_t len) {
213 if (UNLIKELY(pullAtMost(buf, len) != len)) {
214 throw std::out_of_range("underflow");
218 void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
219 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
220 throw std::out_of_range("underflow");
224 void skip(size_t len) {
225 if (UNLIKELY(skipAtMost(len) != len)) {
226 throw std::out_of_range("underflow");
230 size_t pullAtMost(void* buf, size_t len) {
231 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
234 // Fast path: it all fits in one buffer.
235 size_t available = length();
236 if (LIKELY(available >= len)) {
237 memcpy(p, data(), len);
242 memcpy(p, data(), available);
244 if (UNLIKELY(!tryAdvanceBuffer())) {
252 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
255 std::unique_ptr<folly::IOBuf> tmp;
258 // Fast path: it all fits in one buffer.
259 size_t available = length();
260 if (LIKELY(available >= len)) {
261 tmp = crtBuf_->cloneOne();
262 tmp->trimStart(offset_);
263 tmp->trimEnd(tmp->length() - len);
266 buf = std::move(tmp);
268 buf->prependChain(std::move(tmp));
273 tmp = crtBuf_->cloneOne();
274 tmp->trimStart(offset_);
276 buf = std::move(tmp);
278 buf->prependChain(std::move(tmp));
282 if (UNLIKELY(!tryAdvanceBuffer())) {
289 size_t skipAtMost(size_t len) {
292 // Fast path: it all fits in one buffer.
293 size_t available = length();
294 if (LIKELY(available >= len)) {
296 return skipped + len;
299 skipped += available;
300 if (UNLIKELY(!tryAdvanceBuffer())) {
308 * Return the distance between two cursors.
310 size_t operator-(const CursorBase& other) const {
311 BufType *otherBuf = other.crtBuf_;
314 if (otherBuf != crtBuf_) {
315 len += otherBuf->length() - other.offset_;
317 for (otherBuf = otherBuf->next();
318 otherBuf != crtBuf_ && otherBuf != other.buffer_;
319 otherBuf = otherBuf->next()) {
320 len += otherBuf->length();
323 if (otherBuf == other.buffer_) {
324 throw std::out_of_range("wrap-around");
329 if (offset_ < other.offset_) {
330 throw std::out_of_range("underflow");
333 len += offset_ - other.offset_;
340 * Return the distance from the given IOBuf to the this cursor.
342 size_t operator-(const BufType* buf) const {
345 BufType *curBuf = buf;
346 while (curBuf != crtBuf_) {
347 len += curBuf->length();
348 curBuf = curBuf->next();
349 if (curBuf == buf || curBuf == buffer_) {
350 throw std::out_of_range("wrap-around");
368 bool tryAdvanceBuffer() {
369 BufType* nextBuf = crtBuf_->next();
370 if (UNLIKELY(nextBuf == buffer_)) {
371 offset_ = crtBuf_->length();
377 static_cast<Derived*>(this)->advanceDone();
388 template <class Derived>
392 typename std::enable_if<std::is_integral<T>::value>::type
394 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
395 Derived* d = static_cast<Derived*>(this);
396 d->push(u8, sizeof(T));
400 void writeBE(T value) {
401 Derived* d = static_cast<Derived*>(this);
402 d->write(Endian::big(value));
406 void writeLE(T value) {
407 Derived* d = static_cast<Derived*>(this);
408 d->write(Endian::little(value));
411 void push(const uint8_t* buf, size_t len) {
412 Derived* d = static_cast<Derived*>(this);
413 if (d->pushAtMost(buf, len) != len) {
414 throw std::out_of_range("overflow");
419 } // namespace detail
421 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
423 explicit Cursor(const IOBuf* buf)
424 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
426 template <class CursorType>
427 explicit Cursor(CursorType& cursor)
428 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
431 enum class CursorAccess {
436 template <CursorAccess access>
438 : public detail::CursorBase<RWCursor<access>, IOBuf>,
439 public detail::Writable<RWCursor<access>> {
440 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
442 explicit RWCursor(IOBuf* buf)
443 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
444 maybeShared_(true) {}
446 template <class CursorType>
447 explicit RWCursor(CursorType& cursor)
448 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
449 maybeShared_(true) {}
451 * Gather at least n bytes contiguously into the current buffer,
452 * by coalescing subsequent buffers from the chain as necessary.
454 void gather(size_t n) {
455 // Forbid attempts to gather beyond the end of this IOBuf chain.
456 // Otherwise we could try to coalesce the head of the chain and end up
457 // accidentally freeing it, invalidating the pointer owned by external
460 // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
461 // checking. We only have to perform an explicit check here when calling
462 // gather() on a non-head element.
463 if (this->crtBuf_ != this->head() && this->totalLength() < n) {
464 throw std::overflow_error("cannot gather() past the end of the chain");
466 this->crtBuf_->gather(this->offset_ + n);
468 void gatherAtMost(size_t n) {
469 size_t size = std::min(n, this->totalLength());
470 return this->crtBuf_->gather(this->offset_ + size);
473 size_t pushAtMost(const uint8_t* buf, size_t len) {
476 // Fast path: the current buffer is big enough.
477 size_t available = this->length();
478 if (LIKELY(available >= len)) {
479 if (access == CursorAccess::UNSHARE) {
482 memcpy(writableData(), buf, len);
483 this->offset_ += len;
487 if (access == CursorAccess::UNSHARE) {
490 memcpy(writableData(), buf, available);
492 if (UNLIKELY(!this->tryAdvanceBuffer())) {
500 void insert(std::unique_ptr<folly::IOBuf> buf) {
501 folly::IOBuf* nextBuf;
502 if (this->offset_ == 0) {
505 this->crtBuf_->prependChain(std::move(buf));
507 std::unique_ptr<folly::IOBuf> remaining;
508 if (this->crtBuf_->length() - this->offset_ > 0) {
509 // Need to split current IOBuf in two.
510 remaining = this->crtBuf_->cloneOne();
511 remaining->trimStart(this->offset_);
512 nextBuf = remaining.get();
513 buf->prependChain(std::move(remaining));
516 nextBuf = this->crtBuf_->next();
518 this->crtBuf_->trimEnd(this->length());
519 this->crtBuf_->appendChain(std::move(buf));
521 // Jump past the new links
523 this->crtBuf_ = nextBuf;
526 uint8_t* writableData() {
527 return this->crtBuf_->writableData() + this->offset_;
531 void maybeUnshare() {
532 if (UNLIKELY(maybeShared_)) {
533 this->crtBuf_->unshareOne();
534 maybeShared_ = false;
545 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
546 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
549 * Append to the end of a buffer chain, growing the chain (by allocating new
550 * buffers) in increments of at least growth bytes every time. Won't grow
551 * (and push() and ensure() will throw) if growth == 0.
553 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
556 class Appender : public detail::Writable<Appender> {
558 Appender(IOBuf* buf, uint32_t growth)
560 crtBuf_(buf->prev()),
564 uint8_t* writableData() {
565 return crtBuf_->writableTail();
568 size_t length() const {
569 return crtBuf_->tailroom();
573 * Mark n bytes (must be <= length()) as appended, as per the
574 * IOBuf::append() method.
576 void append(size_t n) {
581 * Ensure at least n contiguous bytes available to write.
582 * Postcondition: length() >= n.
584 void ensure(uint32_t n) {
585 if (LIKELY(length() >= n)) {
589 // Waste the rest of the current buffer and allocate a new one.
590 // Don't make it too small, either.
592 throw std::out_of_range("can't grow buffer chain");
595 n = std::max(n, growth_);
596 buffer_->prependChain(IOBuf::create(n));
597 crtBuf_ = buffer_->prev();
600 size_t pushAtMost(const uint8_t* buf, size_t len) {
603 // Fast path: it all fits in one buffer.
604 size_t available = length();
605 if (LIKELY(available >= len)) {
606 memcpy(writableData(), buf, len);
611 memcpy(writableData(), buf, available);
614 if (UNLIKELY(!tryGrowChain())) {
623 bool tryGrowChain() {
624 assert(crtBuf_->next() == buffer_);
629 buffer_->prependChain(IOBuf::create(growth_));
630 crtBuf_ = buffer_->prev();
639 class QueueAppender : public detail::Writable<QueueAppender> {
642 * Create an Appender that writes to a IOBufQueue. When we allocate
643 * space in the queue, we grow no more than growth bytes at once
644 * (unless you call ensure() with a bigger value yourself).
646 QueueAppender(IOBufQueue* queue, uint32_t growth) {
647 reset(queue, growth);
650 void reset(IOBufQueue* queue, uint32_t growth) {
655 uint8_t* writableData() {
656 return static_cast<uint8_t*>(queue_->writableTail());
659 size_t length() const { return queue_->tailroom(); }
661 void append(size_t n) { queue_->postallocate(n); }
663 // Ensure at least n contiguous; can go above growth_, throws if
665 void ensure(uint32_t n) { queue_->preallocate(n, growth_); }
668 typename std::enable_if<std::is_integral<T>::value>::type
671 auto p = queue_->preallocate(sizeof(T), growth_);
672 storeUnaligned(p.first, value);
673 queue_->postallocate(sizeof(T));
677 size_t pushAtMost(const uint8_t* buf, size_t len) {
678 size_t remaining = len;
679 while (remaining != 0) {
680 auto p = queue_->preallocate(std::min(remaining, growth_),
683 memcpy(p.first, buf, p.second);
684 queue_->postallocate(p.second);
686 remaining -= p.second;
692 void insert(std::unique_ptr<folly::IOBuf> buf) {
694 queue_->append(std::move(buf), true);
699 folly::IOBufQueue* queue_;
705 #endif // FOLLY_CURSOR_H