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.
24 #include <type_traits>
26 #include <folly/Likely.h>
27 #include <folly/Memory.h>
28 #include <folly/Portability.h>
29 #include <folly/Range.h>
30 #include <folly/io/IOBuf.h>
31 #include <folly/io/IOBufQueue.h>
32 #include <folly/lang/Bits.h>
33 #include <folly/portability/BitsFunctexcept.h>
36 * Cursor class for fast iteration over IOBuf chains.
38 * Cursor - Read-only access
40 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
41 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
42 * Appender - Write access, assumes private access to IOBuf chain
44 * Note that RW cursors write in the preallocated part of buffers (that is,
45 * between the buffer's data() and tail()), while Appenders append to the end
46 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
47 * automatically adjust the buffer pointers, so you may only use one
48 * Appender with a buffer chain; for this reason, Appenders assume private
49 * access to the buffer (you need to call unshare() yourself if necessary).
56 template <class Derived, class BufType>
58 // Make all the templated classes friends for copy constructor.
59 template <class D, typename B> friend class CursorBase;
61 explicit CursorBase(BufType* buf) : crtBuf_(buf), buffer_(buf) { }
66 * This also allows constructing a CursorBase from other derived types.
67 * For instance, this allows constructing a Cursor from an RWPrivateCursor.
69 template <class OtherDerived, class OtherBuf>
70 explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor)
71 : crtBuf_(cursor.crtBuf_),
72 offset_(cursor.offset_),
73 buffer_(cursor.buffer_) { }
76 * Reset cursor to point to a new buffer.
78 void reset(BufType* buf) {
84 const uint8_t* data() const {
85 return crtBuf_->data() + offset_;
89 * Return the remaining space available in the current IOBuf.
91 * May return 0 if the cursor is at the end of an IOBuf. Use peekBytes()
92 * instead if you want to avoid this. peekBytes() will advance to the next
93 * non-empty IOBuf (up to the end of the chain) if the cursor is currently
94 * pointing at the end of a buffer.
96 size_t length() const {
97 return crtBuf_->length() - offset_;
101 * Return the space available until the end of the entire IOBuf chain.
103 size_t totalLength() const {
104 if (crtBuf_ == buffer_) {
105 return crtBuf_->computeChainDataLength() - offset_;
107 CursorBase end(buffer_->prev());
108 end.offset_ = end.buffer_->length();
113 * Return true if the cursor could advance the specified number of bytes
114 * from its current position.
115 * This is useful for applications that want to do checked reads instead of
116 * catching exceptions and is more efficient than using totalLength as it
117 * walks the minimal set of buffers in the chain to determine the result.
119 bool canAdvance(size_t amount) const {
120 const IOBuf* nextBuf = crtBuf_;
121 size_t available = length();
123 if (available >= amount) {
127 nextBuf = nextBuf->next();
128 available = nextBuf->length();
129 } while (nextBuf != buffer_);
134 * Return true if the cursor is at the end of the entire IOBuf chain.
136 bool isAtEnd() const {
137 // Check for the simple cases first.
138 if (offset_ != crtBuf_->length()) {
141 if (crtBuf_ == buffer_->prev()) {
144 // We are at the end of a buffer, but it isn't the last buffer.
145 // We might still be at the end if the remaining buffers in the chain are
147 const IOBuf* buf = crtBuf_->next();;
148 while (buf != buffer_) {
149 if (buf->length() > 0) {
158 * Advances the cursor to the end of the entire IOBuf chain.
160 void advanceToEnd() {
161 offset_ = buffer_->prev()->length();
162 if (crtBuf_ != buffer_->prev()) {
163 crtBuf_ = buffer_->prev();
164 static_cast<Derived*>(this)->advanceDone();
168 Derived& operator+=(size_t offset) {
169 Derived* p = static_cast<Derived*>(this);
173 Derived operator+(size_t offset) const {
174 Derived other(*this);
179 Derived& operator-=(size_t offset) {
180 Derived* p = static_cast<Derived*>(this);
184 Derived operator-(size_t offset) const {
185 Derived other(*this);
186 other.retreat(offset);
191 * Compare cursors for equality/inequality.
193 * Two cursors are equal if they are pointing to the same location in the
196 bool operator==(const Derived& other) const {
197 return (offset_ == other.offset_) && (crtBuf_ == other.crtBuf_);
199 bool operator!=(const Derived& other) const {
200 return !operator==(other);
204 typename std::enable_if<std::is_arithmetic<T>::value, bool>::type tryRead(
206 if (LIKELY(length() >= sizeof(T))) {
207 val = loadUnaligned<T>(data());
208 offset_ += sizeof(T);
209 advanceBufferIfEmpty();
212 return pullAtMostSlow(&val, sizeof(T)) == sizeof(T);
216 bool tryReadBE(T& val) {
217 const bool result = tryRead(val);
218 val = Endian::big(val);
223 bool tryReadLE(T& val) {
224 const bool result = tryRead(val);
225 val = Endian::little(val);
233 std::__throw_out_of_range("underflow");
240 return Endian::big(read<T>());
245 return Endian::little(read<T>());
249 * Read a fixed-length string.
251 * The std::string-based APIs should probably be avoided unless you
252 * ultimately want the data to live in an std::string. You're better off
253 * using the pull() APIs to copy into a raw buffer otherwise.
255 std::string readFixedString(size_t len) {
258 if (LIKELY(length() >= len)) {
259 str.append(reinterpret_cast<const char*>(data()), len);
261 advanceBufferIfEmpty();
263 readFixedStringSlow(&str, len);
269 * Read a string consisting of bytes until the given terminator character is
270 * seen. Raises an std::length_error if maxLength bytes have been processed
271 * before the terminator is seen.
273 * See comments in readFixedString() about when it's appropriate to use this
276 std::string readTerminatedString(
277 char termChar = '\0',
278 size_t maxLength = std::numeric_limits<size_t>::max());
281 * Read all bytes until the specified predicate returns true.
283 * The predicate will be called on each byte in turn, until it returns false
284 * or until the end of the IOBuf chain is reached.
286 * Returns the result as a string.
288 template <typename Predicate>
289 std::string readWhile(const Predicate& predicate);
292 * Read all bytes until the specified predicate returns true.
294 * This is a more generic version of readWhile() takes an arbitrary Output
295 * object, and calls Output::append() with each chunk of matching data.
297 template <typename Predicate, typename Output>
298 void readWhile(const Predicate& predicate, Output& out);
301 * Skip all bytes until the specified predicate returns true.
303 * The predicate will be called on each byte in turn, until it returns false
304 * or until the end of the IOBuf chain is reached.
306 template <typename Predicate>
307 void skipWhile(const Predicate& predicate);
309 size_t skipAtMost(size_t len) {
310 if (LIKELY(length() >= len)) {
312 advanceBufferIfEmpty();
315 return skipAtMostSlow(len);
318 void skip(size_t len) {
319 if (LIKELY(length() >= len)) {
321 advanceBufferIfEmpty();
327 size_t retreatAtMost(size_t len) {
328 if (len <= offset_) {
332 return retreatAtMostSlow(len);
335 void retreat(size_t len) {
336 if (len <= offset_) {
343 size_t pullAtMost(void* buf, size_t len) {
344 // Fast path: it all fits in one buffer.
345 if (LIKELY(length() >= len)) {
346 memcpy(buf, data(), len);
348 advanceBufferIfEmpty();
351 return pullAtMostSlow(buf, len);
354 void pull(void* buf, size_t len) {
355 if (LIKELY(length() >= len)) {
356 memcpy(buf, data(), len);
358 advanceBufferIfEmpty();
365 * Return the available data in the current buffer.
366 * If you want to gather more data from the chain into a contiguous region
367 * (for hopefully zero-copy access), use gather() before peekBytes().
369 ByteRange peekBytes() {
370 // Ensure that we're pointing to valid data
371 size_t available = length();
372 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
373 available = length();
375 return ByteRange{data(), available};
379 * Alternate version of peekBytes() that returns a std::pair
380 * instead of a ByteRange. (This method pre-dates ByteRange.)
382 * This function will eventually be deprecated.
384 std::pair<const uint8_t*, size_t> peek() {
385 auto bytes = peekBytes();
386 return std::make_pair(bytes.data(), bytes.size());
389 void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
390 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
391 std::__throw_out_of_range("underflow");
395 void clone(folly::IOBuf& buf, size_t len) {
396 if (UNLIKELY(cloneAtMost(buf, len) != len)) {
397 std::__throw_out_of_range("underflow");
401 size_t cloneAtMost(folly::IOBuf& buf, size_t len) {
402 std::unique_ptr<folly::IOBuf> tmp;
404 for (int loopCount = 0; true; ++loopCount) {
405 // Fast path: it all fits in one buffer.
406 size_t available = length();
407 if (LIKELY(available >= len)) {
408 if (loopCount == 0) {
409 crtBuf_->cloneOneInto(buf);
410 buf.trimStart(offset_);
411 buf.trimEnd(buf.length() - len);
413 tmp = crtBuf_->cloneOne();
414 tmp->trimStart(offset_);
415 tmp->trimEnd(tmp->length() - len);
416 buf.prependChain(std::move(tmp));
420 advanceBufferIfEmpty();
424 if (loopCount == 0) {
425 crtBuf_->cloneOneInto(buf);
426 buf.trimStart(offset_);
428 tmp = crtBuf_->cloneOne();
429 tmp->trimStart(offset_);
430 buf.prependChain(std::move(tmp));
434 if (UNLIKELY(!tryAdvanceBuffer())) {
441 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
443 buf = std::make_unique<folly::IOBuf>();
445 return cloneAtMost(*buf, len);
449 * Return the distance between two cursors.
451 size_t operator-(const CursorBase& other) const {
452 BufType *otherBuf = other.crtBuf_;
455 if (otherBuf != crtBuf_) {
456 len += otherBuf->length() - other.offset_;
458 for (otherBuf = otherBuf->next();
459 otherBuf != crtBuf_ && otherBuf != other.buffer_;
460 otherBuf = otherBuf->next()) {
461 len += otherBuf->length();
464 if (otherBuf == other.buffer_) {
465 std::__throw_out_of_range("wrap-around");
470 if (offset_ < other.offset_) {
471 std::__throw_out_of_range("underflow");
474 len += offset_ - other.offset_;
481 * Return the distance from the given IOBuf to the this cursor.
483 size_t operator-(const BufType* buf) const {
486 const BufType* curBuf = buf;
487 while (curBuf != crtBuf_) {
488 len += curBuf->length();
489 curBuf = curBuf->next();
490 if (curBuf == buf || curBuf == buffer_) {
491 std::__throw_out_of_range("wrap-around");
506 bool tryAdvanceBuffer() {
507 BufType* nextBuf = crtBuf_->next();
508 if (UNLIKELY(nextBuf == buffer_)) {
509 offset_ = crtBuf_->length();
515 static_cast<Derived*>(this)->advanceDone();
519 bool tryRetreatBuffer() {
520 if (UNLIKELY(crtBuf_ == buffer_)) {
524 crtBuf_ = crtBuf_->prev();
525 offset_ = crtBuf_->length();
526 static_cast<Derived*>(this)->advanceDone();
530 void advanceBufferIfEmpty() {
540 void readFixedStringSlow(std::string* str, size_t len) {
541 for (size_t available; (available = length()) < len; ) {
542 str->append(reinterpret_cast<const char*>(data()), available);
543 if (UNLIKELY(!tryAdvanceBuffer())) {
544 std::__throw_out_of_range("string underflow");
548 str->append(reinterpret_cast<const char*>(data()), len);
550 advanceBufferIfEmpty();
553 size_t pullAtMostSlow(void* buf, size_t len) {
554 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
556 for (size_t available; (available = length()) < len; ) {
557 memcpy(p, data(), available);
559 if (UNLIKELY(!tryAdvanceBuffer())) {
565 memcpy(p, data(), len);
567 advanceBufferIfEmpty();
571 void pullSlow(void* buf, size_t len) {
572 if (UNLIKELY(pullAtMostSlow(buf, len) != len)) {
573 std::__throw_out_of_range("underflow");
577 size_t skipAtMostSlow(size_t len) {
579 for (size_t available; (available = length()) < len; ) {
580 skipped += available;
581 if (UNLIKELY(!tryAdvanceBuffer())) {
587 advanceBufferIfEmpty();
588 return skipped + len;
591 void skipSlow(size_t len) {
592 if (UNLIKELY(skipAtMostSlow(len) != len)) {
593 std::__throw_out_of_range("underflow");
597 size_t retreatAtMostSlow(size_t len) {
598 size_t retreated = 0;
599 for (size_t available; (available = offset_) < len;) {
600 retreated += available;
601 if (UNLIKELY(!tryRetreatBuffer())) {
607 return retreated + len;
610 void retreatSlow(size_t len) {
611 if (UNLIKELY(retreatAtMostSlow(len) != len)) {
612 std::__throw_out_of_range("underflow");
622 } // namespace detail
624 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
626 explicit Cursor(const IOBuf* buf)
627 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
629 template <class OtherDerived, class OtherBuf>
630 explicit Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
631 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
636 template <class Derived>
640 typename std::enable_if<std::is_arithmetic<T>::value>::type
642 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
643 Derived* d = static_cast<Derived*>(this);
644 d->push(u8, sizeof(T));
648 void writeBE(T value) {
649 Derived* d = static_cast<Derived*>(this);
650 d->write(Endian::big(value));
654 void writeLE(T value) {
655 Derived* d = static_cast<Derived*>(this);
656 d->write(Endian::little(value));
659 void push(const uint8_t* buf, size_t len) {
660 Derived* d = static_cast<Derived*>(this);
661 if (d->pushAtMost(buf, len) != len) {
662 std::__throw_out_of_range("overflow");
666 void push(ByteRange buf) {
667 if (this->pushAtMost(buf) != buf.size()) {
668 std::__throw_out_of_range("overflow");
672 size_t pushAtMost(ByteRange buf) {
673 Derived* d = static_cast<Derived*>(this);
674 return d->pushAtMost(buf.data(), buf.size());
678 * push len bytes of data from input cursor, data could be in an IOBuf chain.
679 * If input cursor contains less than len bytes, or this cursor has less than
680 * len bytes writable space, an out_of_range exception will be thrown.
682 void push(Cursor cursor, size_t len) {
683 if (this->pushAtMost(cursor, len) != len) {
684 std::__throw_out_of_range("overflow");
688 size_t pushAtMost(Cursor cursor, size_t len) {
691 auto currentBuffer = cursor.peekBytes();
692 const uint8_t* crtData = currentBuffer.data();
693 size_t available = currentBuffer.size();
694 if (available == 0) {
695 // end of buffer chain
698 // all data is in current buffer
699 if (available >= len) {
700 this->push(crtData, len);
702 return written + len;
705 // write the whole current IOBuf
706 this->push(crtData, available);
707 cursor.skip(available);
708 written += available;
714 } // namespace detail
716 enum class CursorAccess {
721 template <CursorAccess access>
723 : public detail::CursorBase<RWCursor<access>, IOBuf>,
724 public detail::Writable<RWCursor<access>> {
725 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
727 explicit RWCursor(IOBuf* buf)
728 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
729 maybeShared_(true) {}
731 template <class OtherDerived, class OtherBuf>
732 explicit RWCursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
733 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
734 maybeShared_(true) {}
736 * Gather at least n bytes contiguously into the current buffer,
737 * by coalescing subsequent buffers from the chain as necessary.
739 void gather(size_t n) {
740 // Forbid attempts to gather beyond the end of this IOBuf chain.
741 // Otherwise we could try to coalesce the head of the chain and end up
742 // accidentally freeing it, invalidating the pointer owned by external
745 // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
746 // checking. We only have to perform an explicit check here when calling
747 // gather() on a non-head element.
748 if (this->crtBuf_ != this->head() && this->totalLength() < n) {
749 throw std::overflow_error("cannot gather() past the end of the chain");
751 this->crtBuf_->gather(this->offset_ + n);
753 void gatherAtMost(size_t n) {
754 size_t size = std::min(n, this->totalLength());
755 return this->crtBuf_->gather(this->offset_ + size);
758 using detail::Writable<RWCursor<access>>::pushAtMost;
759 size_t pushAtMost(const uint8_t* buf, size_t len) {
760 // We have to explicitly check for an input length of 0.
761 // We support buf being nullptr in this case, but we need to avoid calling
762 // memcpy() with a null source pointer, since that is undefined behavior
763 // even if the length is 0.
770 // Fast path: the current buffer is big enough.
771 size_t available = this->length();
772 if (LIKELY(available >= len)) {
773 if (access == CursorAccess::UNSHARE) {
776 memcpy(writableData(), buf, len);
777 this->offset_ += len;
781 if (access == CursorAccess::UNSHARE) {
784 memcpy(writableData(), buf, available);
786 if (UNLIKELY(!this->tryAdvanceBuffer())) {
794 void insert(std::unique_ptr<folly::IOBuf> buf) {
795 folly::IOBuf* nextBuf;
796 if (this->offset_ == 0) {
798 nextBuf = this->crtBuf_;
799 this->crtBuf_->prependChain(std::move(buf));
801 std::unique_ptr<folly::IOBuf> remaining;
802 if (this->crtBuf_->length() - this->offset_ > 0) {
803 // Need to split current IOBuf in two.
804 remaining = this->crtBuf_->cloneOne();
805 remaining->trimStart(this->offset_);
806 nextBuf = remaining.get();
807 buf->prependChain(std::move(remaining));
810 nextBuf = this->crtBuf_->next();
812 this->crtBuf_->trimEnd(this->length());
813 this->crtBuf_->appendChain(std::move(buf));
815 // Jump past the new links
817 this->crtBuf_ = nextBuf;
820 uint8_t* writableData() {
821 return this->crtBuf_->writableData() + this->offset_;
825 void maybeUnshare() {
826 if (UNLIKELY(maybeShared_)) {
827 this->crtBuf_->unshareOne();
828 maybeShared_ = false;
839 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
840 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
843 * Append to the end of a buffer chain, growing the chain (by allocating new
844 * buffers) in increments of at least growth bytes every time. Won't grow
845 * (and push() and ensure() will throw) if growth == 0.
847 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
850 class Appender : public detail::Writable<Appender> {
852 Appender(IOBuf* buf, uint64_t growth)
854 crtBuf_(buf->prev()),
858 uint8_t* writableData() {
859 return crtBuf_->writableTail();
862 size_t length() const {
863 return crtBuf_->tailroom();
867 * Mark n bytes (must be <= length()) as appended, as per the
868 * IOBuf::append() method.
870 void append(size_t n) {
875 * Ensure at least n contiguous bytes available to write.
876 * Postcondition: length() >= n.
878 void ensure(uint64_t n) {
879 if (LIKELY(length() >= n)) {
883 // Waste the rest of the current buffer and allocate a new one.
884 // Don't make it too small, either.
886 std::__throw_out_of_range("can't grow buffer chain");
889 n = std::max(n, growth_);
890 buffer_->prependChain(IOBuf::create(n));
891 crtBuf_ = buffer_->prev();
894 using detail::Writable<Appender>::pushAtMost;
895 size_t pushAtMost(const uint8_t* buf, size_t len) {
896 // We have to explicitly check for an input length of 0.
897 // We support buf being nullptr in this case, but we need to avoid calling
898 // memcpy() with a null source pointer, since that is undefined behavior
899 // even if the length is 0.
906 // Fast path: it all fits in one buffer.
907 size_t available = length();
908 if (LIKELY(available >= len)) {
909 memcpy(writableData(), buf, len);
914 memcpy(writableData(), buf, available);
917 if (UNLIKELY(!tryGrowChain())) {
926 * Append to the end of this buffer, using a printf() style
929 * Note that folly/Format.h provides nicer and more type-safe mechanisms
930 * for formatting strings, which should generally be preferred over
931 * printf-style formatting. Appender objects can be used directly as an
932 * output argument for Formatter objects. For example:
934 * Appender app(&iobuf);
935 * format("{} {}", "hello", "world")(app);
937 * However, printf-style strings are still needed when dealing with existing
938 * third-party code in some cases.
940 * This will always add a nul-terminating character after the end
941 * of the output. However, the buffer data length will only be updated to
942 * include the data itself. The nul terminator will be the first byte in the
945 * This method may throw exceptions on error.
947 void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)
948 FOLLY_PRINTF_FORMAT_ATTR(2, 3);
950 void vprintf(const char* fmt, va_list ap);
953 * Calling an Appender object with a StringPiece will append the string
954 * piece. This allows Appender objects to be used directly with
957 void operator()(StringPiece sp) {
962 bool tryGrowChain() {
963 assert(crtBuf_->next() == buffer_);
968 buffer_->prependChain(IOBuf::create(growth_));
969 crtBuf_ = buffer_->prev();
978 class QueueAppender : public detail::Writable<QueueAppender> {
981 * Create an Appender that writes to a IOBufQueue. When we allocate
982 * space in the queue, we grow no more than growth bytes at once
983 * (unless you call ensure() with a bigger value yourself).
985 QueueAppender(IOBufQueue* queue, uint64_t growth)
986 : queueCache_(queue), growth_(growth) {}
988 void reset(IOBufQueue* queue, uint64_t growth) {
989 queueCache_.reset(queue);
993 uint8_t* writableData() {
994 return queueCache_.writableData();
998 return queueCache_.length();
1001 void append(size_t n) {
1002 queueCache_.append(n);
1005 // Ensure at least n contiguous; can go above growth_, throws if
1007 void ensure(size_t n) {
1014 typename std::enable_if<std::is_arithmetic<T>::value>::type write(T value) {
1016 if (length() >= sizeof(T)) {
1017 storeUnaligned(queueCache_.writableData(), value);
1018 queueCache_.appendUnsafe(sizeof(T));
1020 writeSlow<T>(value);
1024 using detail::Writable<QueueAppender>::pushAtMost;
1025 size_t pushAtMost(const uint8_t* buf, size_t len) {
1026 // Fill the current buffer
1027 const size_t copyLength = std::min(len, length());
1028 if (copyLength != 0) {
1029 memcpy(writableData(), buf, copyLength);
1030 queueCache_.appendUnsafe(copyLength);
1033 size_t remaining = len - copyLength;
1034 // Allocate more buffers as necessary
1035 while (remaining != 0) {
1036 auto p = queueCache_.queue()->preallocate(
1037 std::min(remaining, growth_), growth_, remaining);
1038 memcpy(p.first, buf, p.second);
1039 queueCache_.queue()->postallocate(p.second);
1041 remaining -= p.second;
1046 void insert(std::unique_ptr<folly::IOBuf> buf) {
1048 queueCache_.queue()->append(std::move(buf), true);
1052 void insert(const folly::IOBuf& buf) {
1053 insert(buf.clone());
1057 folly::IOBufQueue::WritableRangeCache queueCache_{nullptr};
1060 FOLLY_NOINLINE void ensureSlow(size_t n) {
1061 queueCache_.queue()->preallocate(n, growth_);
1062 queueCache_.fillCache();
1066 typename std::enable_if<std::is_arithmetic<T>::value>::type FOLLY_NOINLINE
1067 writeSlow(T value) {
1068 queueCache_.queue()->preallocate(sizeof(T), growth_);
1069 queueCache_.fillCache();
1071 storeUnaligned(queueCache_.writableData(), value);
1072 queueCache_.appendUnsafe(sizeof(T));
1077 } // namespace folly
1079 #include <folly/io/Cursor-inl.h>