2 * Copyright 2013-present 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.
23 #include <type_traits>
25 #include <folly/Likely.h>
26 #include <folly/Memory.h>
27 #include <folly/Portability.h>
28 #include <folly/Range.h>
29 #include <folly/io/IOBuf.h>
30 #include <folly/io/IOBufQueue.h>
31 #include <folly/lang/Bits.h>
32 #include <folly/portability/BitsFunctexcept.h>
35 * Cursor class for fast iteration over IOBuf chains.
37 * Cursor - Read-only access
39 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
40 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
41 * Appender - Write access, assumes private access to IOBuf chain
43 * Note that RW cursors write in the preallocated part of buffers (that is,
44 * between the buffer's data() and tail()), while Appenders append to the end
45 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
46 * automatically adjust the buffer pointers, so you may only use one
47 * Appender with a buffer chain; for this reason, Appenders assume private
48 * access to the buffer (you need to call unshare() yourself if necessary).
55 template <class Derived, class BufType>
57 // Make all the templated classes friends for copy constructor.
58 template <class D, typename B> friend class CursorBase;
60 explicit CursorBase(BufType* buf) : crtBuf_(buf), buffer_(buf) {
62 crtPos_ = crtBegin_ = crtBuf_->data();
63 crtEnd_ = crtBuf_->tail();
70 * This also allows constructing a CursorBase from other derived types.
71 * For instance, this allows constructing a Cursor from an RWPrivateCursor.
73 template <class OtherDerived, class OtherBuf>
74 explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor)
75 : crtBuf_(cursor.crtBuf_),
76 buffer_(cursor.buffer_),
77 crtBegin_(cursor.crtBegin_),
78 crtEnd_(cursor.crtEnd_),
79 crtPos_(cursor.crtPos_),
80 absolutePos_(cursor.absolutePos_) {}
83 * Reset cursor to point to a new buffer.
85 void reset(BufType* buf) {
90 crtPos_ = crtBegin_ = crtBuf_->data();
91 crtEnd_ = crtBuf_->tail();
96 * Get the current Cursor position relative to the head of IOBuf chain.
98 size_t getCurrentPosition() const {
100 return (crtPos_ - crtBegin_) + absolutePos_;
103 const uint8_t* data() const {
109 * Return the remaining space available in the current IOBuf.
111 * May return 0 if the cursor is at the end of an IOBuf. Use peekBytes()
112 * instead if you want to avoid this. peekBytes() will advance to the next
113 * non-empty IOBuf (up to the end of the chain) if the cursor is currently
114 * pointing at the end of a buffer.
116 size_t length() const {
118 return crtEnd_ - crtPos_;
122 * Return the space available until the end of the entire IOBuf chain.
124 size_t totalLength() const {
125 if (crtBuf_ == buffer_) {
126 return crtBuf_->computeChainDataLength() - (crtPos_ - crtBegin_);
128 CursorBase end(buffer_->prev());
129 end.crtPos_ = end.crtEnd_;
134 * Return true if the cursor could advance the specified number of bytes
135 * from its current position.
136 * This is useful for applications that want to do checked reads instead of
137 * catching exceptions and is more efficient than using totalLength as it
138 * walks the minimal set of buffers in the chain to determine the result.
140 bool canAdvance(size_t amount) const {
141 const IOBuf* nextBuf = crtBuf_;
142 size_t available = length();
144 if (available >= amount) {
148 nextBuf = nextBuf->next();
149 available = nextBuf->length();
150 } while (nextBuf != buffer_);
155 * Return true if the cursor is at the end of the entire IOBuf chain.
157 bool isAtEnd() const {
159 // Check for the simple cases first.
160 if (crtPos_ != crtEnd_) {
163 if (crtBuf_ == buffer_->prev()) {
166 // We are at the end of a buffer, but it isn't the last buffer.
167 // We might still be at the end if the remaining buffers in the chain are
169 const IOBuf* buf = crtBuf_->next();;
170 while (buf != buffer_) {
171 if (buf->length() > 0) {
180 * Advances the cursor to the end of the entire IOBuf chain.
182 void advanceToEnd() {
183 // Simple case, we're already in the last IOBuf.
184 if (crtBuf_ == buffer_->prev()) {
189 auto* nextBuf = crtBuf_->next();
190 while (nextBuf != buffer_) {
191 absolutePos_ += crtEnd_ - crtBegin_;
194 nextBuf = crtBuf_->next();
195 crtBegin_ = crtBuf_->data();
196 crtPos_ = crtEnd_ = crtBuf_->tail();
198 static_cast<Derived*>(this)->advanceDone();
202 Derived& operator+=(size_t offset) {
203 Derived* p = static_cast<Derived*>(this);
207 Derived operator+(size_t offset) const {
208 Derived other(*this);
213 Derived& operator-=(size_t offset) {
214 Derived* p = static_cast<Derived*>(this);
218 Derived operator-(size_t offset) const {
219 Derived other(*this);
220 other.retreat(offset);
225 * Compare cursors for equality/inequality.
227 * Two cursors are equal if they are pointing to the same location in the
230 bool operator==(const Derived& other) const {
231 const IOBuf* crtBuf = crtBuf_;
232 auto crtPos = crtPos_;
233 // We can be pointing to the end of a buffer chunk, find first non-empty.
234 while (crtPos == crtBuf->tail() && crtBuf != buffer_->prev()) {
235 crtBuf = crtBuf->next();
236 crtPos = crtBuf->data();
239 const IOBuf* crtBufOther = other.crtBuf_;
240 auto crtPosOther = other.crtPos_;
241 // We can be pointing to the end of a buffer chunk, find first non-empty.
242 while (crtPosOther == crtBufOther->tail() &&
243 crtBufOther != other.buffer_->prev()) {
244 crtBufOther = crtBufOther->next();
245 crtPosOther = crtBufOther->data();
247 return (crtPos == crtPosOther) && (crtBuf == crtBufOther);
249 bool operator!=(const Derived& other) const {
250 return !operator==(other);
254 typename std::enable_if<std::is_arithmetic<T>::value, bool>::type tryRead(
256 if (LIKELY(crtPos_ + sizeof(T) <= crtEnd_)) {
257 val = loadUnaligned<T>(data());
258 crtPos_ += sizeof(T);
261 return pullAtMostSlow(&val, sizeof(T)) == sizeof(T);
265 bool tryReadBE(T& val) {
266 const bool result = tryRead(val);
267 val = Endian::big(val);
272 bool tryReadLE(T& val) {
273 const bool result = tryRead(val);
274 val = Endian::little(val);
280 if (LIKELY(crtPos_ + sizeof(T) <= crtEnd_)) {
281 T val = loadUnaligned<T>(data());
282 crtPos_ += sizeof(T);
285 return readSlow<T>();
291 return Endian::big(read<T>());
296 return Endian::little(read<T>());
300 * Read a fixed-length string.
302 * The std::string-based APIs should probably be avoided unless you
303 * ultimately want the data to live in an std::string. You're better off
304 * using the pull() APIs to copy into a raw buffer otherwise.
306 std::string readFixedString(size_t len) {
309 if (LIKELY(length() >= len)) {
310 str.append(reinterpret_cast<const char*>(data()), len);
313 readFixedStringSlow(&str, len);
319 * Read a string consisting of bytes until the given terminator character is
320 * seen. Raises an std::length_error if maxLength bytes have been processed
321 * before the terminator is seen.
323 * See comments in readFixedString() about when it's appropriate to use this
326 std::string readTerminatedString(
327 char termChar = '\0',
328 size_t maxLength = std::numeric_limits<size_t>::max());
331 * Read all bytes until the specified predicate returns true.
333 * The predicate will be called on each byte in turn, until it returns false
334 * or until the end of the IOBuf chain is reached.
336 * Returns the result as a string.
338 template <typename Predicate>
339 std::string readWhile(const Predicate& predicate);
342 * Read all bytes until the specified predicate returns true.
344 * This is a more generic version of readWhile() takes an arbitrary Output
345 * object, and calls Output::append() with each chunk of matching data.
347 template <typename Predicate, typename Output>
348 void readWhile(const Predicate& predicate, Output& out);
351 * Skip all bytes until the specified predicate returns true.
353 * The predicate will be called on each byte in turn, until it returns false
354 * or until the end of the IOBuf chain is reached.
356 template <typename Predicate>
357 void skipWhile(const Predicate& predicate);
359 size_t skipAtMost(size_t len) {
361 if (LIKELY(crtPos_ + len < crtEnd_)) {
365 return skipAtMostSlow(len);
368 void skip(size_t len) {
370 if (LIKELY(crtPos_ + len < crtEnd_)) {
378 * Skip bytes in the current IOBuf without advancing to the next one.
379 * Precondition: length() >= len
381 void skipNoAdvance(size_t len) {
382 DCHECK_LE(len, length());
386 size_t retreatAtMost(size_t len) {
388 if (len <= static_cast<size_t>(crtPos_ - crtBegin_)) {
392 return retreatAtMostSlow(len);
395 void retreat(size_t len) {
397 if (len <= static_cast<size_t>(crtPos_ - crtBegin_)) {
404 size_t pullAtMost(void* buf, size_t len) {
406 // Fast path: it all fits in one buffer.
407 if (LIKELY(crtPos_ + len <= crtEnd_)) {
408 memcpy(buf, data(), len);
412 return pullAtMostSlow(buf, len);
415 void pull(void* buf, size_t len) {
417 if (LIKELY(crtPos_ + len <= crtEnd_)) {
418 memcpy(buf, data(), len);
426 * Return the available data in the current buffer.
427 * If you want to gather more data from the chain into a contiguous region
428 * (for hopefully zero-copy access), use gather() before peekBytes().
430 ByteRange peekBytes() {
431 // Ensure that we're pointing to valid data
432 size_t available = length();
433 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
434 available = length();
436 return ByteRange{data(), available};
440 * Alternate version of peekBytes() that returns a std::pair
441 * instead of a ByteRange. (This method pre-dates ByteRange.)
443 * This function will eventually be deprecated.
445 std::pair<const uint8_t*, size_t> peek() {
446 auto bytes = peekBytes();
447 return std::make_pair(bytes.data(), bytes.size());
450 void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
451 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
452 std::__throw_out_of_range("underflow");
456 void clone(folly::IOBuf& buf, size_t len) {
457 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
458 std::__throw_out_of_range("underflow");
462 size_t cloneAtMost(folly::IOBuf& buf, size_t len) {
463 // We might be at the end of buffer.
464 advanceBufferIfEmpty();
466 std::unique_ptr<folly::IOBuf> tmp;
468 for (int loopCount = 0; true; ++loopCount) {
469 // Fast path: it all fits in one buffer.
470 size_t available = length();
471 if (LIKELY(available >= len)) {
472 if (loopCount == 0) {
473 crtBuf_->cloneOneInto(buf);
474 buf.trimStart(crtPos_ - crtBegin_);
475 buf.trimEnd(buf.length() - len);
477 tmp = crtBuf_->cloneOne();
478 tmp->trimStart(crtPos_ - crtBegin_);
479 tmp->trimEnd(tmp->length() - len);
480 buf.prependChain(std::move(tmp));
484 advanceBufferIfEmpty();
488 if (loopCount == 0) {
489 crtBuf_->cloneOneInto(buf);
490 buf.trimStart(crtPos_ - crtBegin_);
492 tmp = crtBuf_->cloneOne();
493 tmp->trimStart(crtPos_ - crtBegin_);
494 buf.prependChain(std::move(tmp));
498 if (UNLIKELY(!tryAdvanceBuffer())) {
505 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
507 buf = std::make_unique<folly::IOBuf>();
509 return cloneAtMost(*buf, len);
513 * Return the distance between two cursors.
515 size_t operator-(const CursorBase& other) const {
516 BufType *otherBuf = other.crtBuf_;
519 if (otherBuf != crtBuf_) {
520 len += other.crtEnd_ - other.crtPos_;
522 for (otherBuf = otherBuf->next();
523 otherBuf != crtBuf_ && otherBuf != other.buffer_;
524 otherBuf = otherBuf->next()) {
525 len += otherBuf->length();
528 if (otherBuf == other.buffer_) {
529 std::__throw_out_of_range("wrap-around");
532 len += crtPos_ - crtBegin_;
534 if (crtPos_ < other.crtPos_) {
535 std::__throw_out_of_range("underflow");
538 len += crtPos_ - other.crtPos_;
545 * Return the distance from the given IOBuf to the this cursor.
547 size_t operator-(const BufType* buf) const {
550 const BufType* curBuf = buf;
551 while (curBuf != crtBuf_) {
552 len += curBuf->length();
553 curBuf = curBuf->next();
554 if (curBuf == buf || curBuf == buffer_) {
555 std::__throw_out_of_range("wrap-around");
559 len += crtPos_ - crtBegin_;
564 void dcheckIntegrity() const {
565 DCHECK(crtBegin_ <= crtPos_ && crtPos_ <= crtEnd_);
566 DCHECK(crtBuf_ == nullptr || crtBegin_ == crtBuf_->data());
568 crtBuf_ == nullptr ||
569 (uint64_t)(crtEnd_ - crtBegin_) == crtBuf_->length());
578 bool tryAdvanceBuffer() {
579 BufType* nextBuf = crtBuf_->next();
580 if (UNLIKELY(nextBuf == buffer_)) {
585 absolutePos_ += crtEnd_ - crtBegin_;
587 crtPos_ = crtBegin_ = crtBuf_->data();
588 crtEnd_ = crtBuf_->tail();
589 static_cast<Derived*>(this)->advanceDone();
593 bool tryRetreatBuffer() {
594 if (UNLIKELY(crtBuf_ == buffer_)) {
598 crtBuf_ = crtBuf_->prev();
599 crtBegin_ = crtBuf_->data();
600 crtPos_ = crtEnd_ = crtBuf_->tail();
601 absolutePos_ -= crtEnd_ - crtBegin_;
602 static_cast<Derived*>(this)->advanceDone();
606 void advanceBufferIfEmpty() {
608 if (crtPos_ == crtEnd_) {
615 const uint8_t* crtBegin_{nullptr};
616 const uint8_t* crtEnd_{nullptr};
617 const uint8_t* crtPos_{nullptr};
618 size_t absolutePos_{0};
622 FOLLY_NOINLINE T readSlow() {
624 pullSlow(&val, sizeof(T));
628 void readFixedStringSlow(std::string* str, size_t len) {
629 for (size_t available; (available = length()) < len; ) {
630 str->append(reinterpret_cast<const char*>(data()), available);
631 if (UNLIKELY(!tryAdvanceBuffer())) {
632 std::__throw_out_of_range("string underflow");
636 str->append(reinterpret_cast<const char*>(data()), len);
638 advanceBufferIfEmpty();
641 size_t pullAtMostSlow(void* buf, size_t len) {
642 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
644 for (size_t available; (available = length()) < len; ) {
645 memcpy(p, data(), available);
647 if (UNLIKELY(!tryAdvanceBuffer())) {
653 memcpy(p, data(), len);
655 advanceBufferIfEmpty();
659 void pullSlow(void* buf, size_t len) {
660 if (UNLIKELY(pullAtMostSlow(buf, len) != len)) {
661 std::__throw_out_of_range("underflow");
665 size_t skipAtMostSlow(size_t len) {
667 for (size_t available; (available = length()) < len; ) {
668 skipped += available;
669 if (UNLIKELY(!tryAdvanceBuffer())) {
675 advanceBufferIfEmpty();
676 return skipped + len;
679 void skipSlow(size_t len) {
680 if (UNLIKELY(skipAtMostSlow(len) != len)) {
681 std::__throw_out_of_range("underflow");
685 size_t retreatAtMostSlow(size_t len) {
686 size_t retreated = 0;
687 for (size_t available; (available = crtPos_ - crtBegin_) < len;) {
688 retreated += available;
689 if (UNLIKELY(!tryRetreatBuffer())) {
695 return retreated + len;
698 void retreatSlow(size_t len) {
699 if (UNLIKELY(retreatAtMostSlow(len) != len)) {
700 std::__throw_out_of_range("underflow");
708 } // namespace detail
710 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
712 explicit Cursor(const IOBuf* buf)
713 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
715 template <class OtherDerived, class OtherBuf>
716 explicit Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
717 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
722 template <class Derived>
726 typename std::enable_if<std::is_arithmetic<T>::value>::type
728 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
729 Derived* d = static_cast<Derived*>(this);
730 d->push(u8, sizeof(T));
734 void writeBE(T value) {
735 Derived* d = static_cast<Derived*>(this);
736 d->write(Endian::big(value));
740 void writeLE(T value) {
741 Derived* d = static_cast<Derived*>(this);
742 d->write(Endian::little(value));
745 void push(const uint8_t* buf, size_t len) {
746 Derived* d = static_cast<Derived*>(this);
747 if (d->pushAtMost(buf, len) != len) {
748 std::__throw_out_of_range("overflow");
752 void push(ByteRange buf) {
753 if (this->pushAtMost(buf) != buf.size()) {
754 std::__throw_out_of_range("overflow");
758 size_t pushAtMost(ByteRange buf) {
759 Derived* d = static_cast<Derived*>(this);
760 return d->pushAtMost(buf.data(), buf.size());
764 * push len bytes of data from input cursor, data could be in an IOBuf chain.
765 * If input cursor contains less than len bytes, or this cursor has less than
766 * len bytes writable space, an out_of_range exception will be thrown.
768 void push(Cursor cursor, size_t len) {
769 if (this->pushAtMost(cursor, len) != len) {
770 std::__throw_out_of_range("overflow");
774 size_t pushAtMost(Cursor cursor, size_t len) {
777 auto currentBuffer = cursor.peekBytes();
778 const uint8_t* crtData = currentBuffer.data();
779 size_t available = currentBuffer.size();
780 if (available == 0) {
781 // end of buffer chain
784 // all data is in current buffer
785 if (available >= len) {
786 this->push(crtData, len);
788 return written + len;
791 // write the whole current IOBuf
792 this->push(crtData, available);
793 cursor.skip(available);
794 written += available;
800 } // namespace detail
802 enum class CursorAccess {
807 template <CursorAccess access>
809 : public detail::CursorBase<RWCursor<access>, IOBuf>,
810 public detail::Writable<RWCursor<access>> {
811 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
813 explicit RWCursor(IOBuf* buf)
814 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
815 maybeShared_(true) {}
817 template <class OtherDerived, class OtherBuf>
818 explicit RWCursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
819 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
820 maybeShared_(true) {}
822 * Gather at least n bytes contiguously into the current buffer,
823 * by coalescing subsequent buffers from the chain as necessary.
825 void gather(size_t n) {
826 // Forbid attempts to gather beyond the end of this IOBuf chain.
827 // Otherwise we could try to coalesce the head of the chain and end up
828 // accidentally freeing it, invalidating the pointer owned by external
831 // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
832 // checking. We only have to perform an explicit check here when calling
833 // gather() on a non-head element.
834 if (this->crtBuf_ != this->head() && this->totalLength() < n) {
835 throw std::overflow_error("cannot gather() past the end of the chain");
837 size_t offset = this->crtPos_ - this->crtBegin_;
838 this->crtBuf_->gather(offset + n);
839 this->crtBegin_ = this->crtBuf_->data();
840 this->crtEnd_ = this->crtBuf_->tail();
841 this->crtPos_ = this->crtBegin_ + offset;
843 void gatherAtMost(size_t n) {
844 this->dcheckIntegrity();
845 size_t size = std::min(n, this->totalLength());
846 size_t offset = this->crtPos_ - this->crtBegin_;
847 this->crtBuf_->gather(offset + size);
848 this->crtBegin_ = this->crtBuf_->data();
849 this->crtEnd_ = this->crtBuf_->tail();
850 this->crtPos_ = this->crtBegin_ + offset;
853 using detail::Writable<RWCursor<access>>::pushAtMost;
854 size_t pushAtMost(const uint8_t* buf, size_t len) {
855 // We have to explicitly check for an input length of 0.
856 // We support buf being nullptr in this case, but we need to avoid calling
857 // memcpy() with a null source pointer, since that is undefined behavior
858 // even if the length is 0.
865 // Fast path: the current buffer is big enough.
866 size_t available = this->length();
867 if (LIKELY(available >= len)) {
868 if (access == CursorAccess::UNSHARE) {
871 memcpy(writableData(), buf, len);
872 this->crtPos_ += len;
876 if (access == CursorAccess::UNSHARE) {
879 memcpy(writableData(), buf, available);
881 if (UNLIKELY(!this->tryAdvanceBuffer())) {
889 void insert(std::unique_ptr<folly::IOBuf> buf) {
890 this->dcheckIntegrity();
891 this->absolutePos_ += buf->computeChainDataLength();
892 if (this->crtPos_ == this->crtBegin_ && this->crtBuf_ != this->buffer_) {
894 this->crtBuf_->prependChain(std::move(buf));
897 std::unique_ptr<folly::IOBuf> remaining;
898 if (this->crtPos_ != this->crtEnd_) {
899 // Need to split current IOBuf in two.
900 remaining = this->crtBuf_->cloneOne();
901 remaining->trimStart(this->crtPos_ - this->crtBegin_);
902 nextBuf = remaining.get();
903 buf->prependChain(std::move(remaining));
906 nextBuf = this->crtBuf_->next();
908 this->crtBuf_->trimEnd(this->length());
909 this->absolutePos_ += this->crtPos_ - this->crtBegin_;
910 this->crtBuf_->appendChain(std::move(buf));
912 if (nextBuf == this->buffer_) {
913 // We've just appended to the end of the buffer, so advance to the end.
914 this->crtBuf_ = this->buffer_->prev();
915 this->crtBegin_ = this->crtBuf_->data();
916 this->crtPos_ = this->crtEnd_ = this->crtBuf_->tail();
917 // This has already been accounted for, so remove it.
918 this->absolutePos_ -= this->crtEnd_ - this->crtBegin_;
920 // Jump past the new links
921 this->crtBuf_ = nextBuf;
922 this->crtPos_ = this->crtBegin_ = this->crtBuf_->data();
923 this->crtEnd_ = this->crtBuf_->tail();
928 uint8_t* writableData() {
929 this->dcheckIntegrity();
930 return this->crtBuf_->writableData() + (this->crtPos_ - this->crtBegin_);
934 void maybeUnshare() {
935 if (UNLIKELY(maybeShared_)) {
936 size_t offset = this->crtPos_ - this->crtBegin_;
937 this->crtBuf_->unshareOne();
938 this->crtBegin_ = this->crtBuf_->data();
939 this->crtEnd_ = this->crtBuf_->tail();
940 this->crtPos_ = this->crtBegin_ + offset;
941 maybeShared_ = false;
952 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
953 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
956 * Append to the end of a buffer chain, growing the chain (by allocating new
957 * buffers) in increments of at least growth bytes every time. Won't grow
958 * (and push() and ensure() will throw) if growth == 0.
960 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
963 class Appender : public detail::Writable<Appender> {
965 Appender(IOBuf* buf, uint64_t growth)
967 crtBuf_(buf->prev()),
971 uint8_t* writableData() {
972 return crtBuf_->writableTail();
975 size_t length() const {
976 return crtBuf_->tailroom();
980 * Mark n bytes (must be <= length()) as appended, as per the
981 * IOBuf::append() method.
983 void append(size_t n) {
988 * Ensure at least n contiguous bytes available to write.
989 * Postcondition: length() >= n.
991 void ensure(uint64_t n) {
992 if (LIKELY(length() >= n)) {
996 // Waste the rest of the current buffer and allocate a new one.
997 // Don't make it too small, either.
999 std::__throw_out_of_range("can't grow buffer chain");
1002 n = std::max(n, growth_);
1003 buffer_->prependChain(IOBuf::create(n));
1004 crtBuf_ = buffer_->prev();
1007 using detail::Writable<Appender>::pushAtMost;
1008 size_t pushAtMost(const uint8_t* buf, size_t len) {
1009 // We have to explicitly check for an input length of 0.
1010 // We support buf being nullptr in this case, but we need to avoid calling
1011 // memcpy() with a null source pointer, since that is undefined behavior
1012 // even if the length is 0.
1019 // Fast path: it all fits in one buffer.
1020 size_t available = length();
1021 if (LIKELY(available >= len)) {
1022 memcpy(writableData(), buf, len);
1024 return copied + len;
1027 memcpy(writableData(), buf, available);
1029 copied += available;
1030 if (UNLIKELY(!tryGrowChain())) {
1039 * Append to the end of this buffer, using a printf() style
1042 * Note that folly/Format.h provides nicer and more type-safe mechanisms
1043 * for formatting strings, which should generally be preferred over
1044 * printf-style formatting. Appender objects can be used directly as an
1045 * output argument for Formatter objects. For example:
1047 * Appender app(&iobuf);
1048 * format("{} {}", "hello", "world")(app);
1050 * However, printf-style strings are still needed when dealing with existing
1051 * third-party code in some cases.
1053 * This will always add a nul-terminating character after the end
1054 * of the output. However, the buffer data length will only be updated to
1055 * include the data itself. The nul terminator will be the first byte in the
1058 * This method may throw exceptions on error.
1060 void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)
1061 FOLLY_PRINTF_FORMAT_ATTR(2, 3);
1063 void vprintf(const char* fmt, va_list ap);
1066 * Calling an Appender object with a StringPiece will append the string
1067 * piece. This allows Appender objects to be used directly with
1070 void operator()(StringPiece sp) {
1071 push(ByteRange(sp));
1075 bool tryGrowChain() {
1076 assert(crtBuf_->next() == buffer_);
1081 buffer_->prependChain(IOBuf::create(growth_));
1082 crtBuf_ = buffer_->prev();
1091 class QueueAppender : public detail::Writable<QueueAppender> {
1094 * Create an Appender that writes to a IOBufQueue. When we allocate
1095 * space in the queue, we grow no more than growth bytes at once
1096 * (unless you call ensure() with a bigger value yourself).
1098 QueueAppender(IOBufQueue* queue, uint64_t growth)
1099 : queueCache_(queue), growth_(growth) {}
1101 void reset(IOBufQueue* queue, uint64_t growth) {
1102 queueCache_.reset(queue);
1106 uint8_t* writableData() {
1107 return queueCache_.writableData();
1111 return queueCache_.length();
1114 void append(size_t n) {
1115 queueCache_.append(n);
1118 // Ensure at least n contiguous; can go above growth_, throws if
1120 void ensure(size_t n) {
1127 typename std::enable_if<std::is_arithmetic<T>::value>::type write(T value) {
1129 if (length() >= sizeof(T)) {
1130 storeUnaligned(queueCache_.writableData(), value);
1131 queueCache_.appendUnsafe(sizeof(T));
1133 writeSlow<T>(value);
1137 using detail::Writable<QueueAppender>::pushAtMost;
1138 size_t pushAtMost(const uint8_t* buf, size_t len) {
1139 // Fill the current buffer
1140 const size_t copyLength = std::min(len, length());
1141 if (copyLength != 0) {
1142 memcpy(writableData(), buf, copyLength);
1143 queueCache_.appendUnsafe(copyLength);
1146 size_t remaining = len - copyLength;
1147 // Allocate more buffers as necessary
1148 while (remaining != 0) {
1149 auto p = queueCache_.queue()->preallocate(
1150 std::min(remaining, growth_), growth_, remaining);
1151 memcpy(p.first, buf, p.second);
1152 queueCache_.queue()->postallocate(p.second);
1154 remaining -= p.second;
1159 void insert(std::unique_ptr<folly::IOBuf> buf) {
1161 queueCache_.queue()->append(std::move(buf), true);
1165 void insert(const folly::IOBuf& buf) {
1166 insert(buf.clone());
1170 folly::IOBufQueue::WritableRangeCache queueCache_{nullptr};
1173 FOLLY_NOINLINE void ensureSlow(size_t n) {
1174 queueCache_.queue()->preallocate(n, growth_);
1175 queueCache_.fillCache();
1179 typename std::enable_if<std::is_arithmetic<T>::value>::type FOLLY_NOINLINE
1180 writeSlow(T value) {
1181 queueCache_.queue()->preallocate(sizeof(T), growth_);
1182 queueCache_.fillCache();
1184 storeUnaligned(queueCache_.writableData(), value);
1185 queueCache_.appendUnsafe(sizeof(T));
1190 } // namespace folly
1192 #include <folly/io/Cursor-inl.h>