CompactProtocol: more improvements
[folly.git] / folly / io / Cursor.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #ifndef FOLLY_CURSOR_H
18 #define FOLLY_CURSOR_H
19
20 #include <assert.h>
21 #include <cstdarg>
22 #include <stdexcept>
23 #include <string.h>
24 #include <type_traits>
25 #include <memory>
26
27 #include <folly/Bits.h>
28 #include <folly/io/IOBuf.h>
29 #include <folly/io/IOBufQueue.h>
30 #include <folly/Likely.h>
31 #include <folly/Memory.h>
32 #include <folly/Portability.h>
33 #include <folly/Range.h>
34
35 /**
36  * Cursor class for fast iteration over IOBuf chains.
37  *
38  * Cursor - Read-only access
39  *
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 chian
43  *
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).
50  **/
51 namespace folly { namespace io {
52 namespace detail {
53
54 template <class Derived, typename BufType>
55 class CursorBase {
56  public:
57   const uint8_t* data() const {
58     return crtBuf_->data() + offset_;
59   }
60
61   /*
62    * Return the remaining space available in the current IOBuf.
63    *
64    * May return 0 if the cursor is at the end of an IOBuf.  Use peek() instead
65    * if you want to avoid this.  peek() will advance to the next non-empty
66    * IOBuf (up to the end of the chain) if the cursor is currently pointing at
67    * the end of a buffer.
68    */
69   size_t length() const {
70     return crtBuf_->length() - offset_;
71   }
72
73   /*
74    * Return the space available until the end of the entire IOBuf chain.
75    */
76   size_t totalLength() const {
77     if (crtBuf_ == buffer_) {
78       return crtBuf_->computeChainDataLength() - offset_;
79     }
80     CursorBase end(buffer_->prev());
81     end.offset_ = end.buffer_->length();
82     return end - *this;
83   }
84
85   Derived& operator+=(size_t offset) {
86     Derived* p = static_cast<Derived*>(this);
87     p->skip(offset);
88     return *p;
89   }
90   Derived operator+(size_t offset) const {
91     Derived other(*this);
92     other.skip(offset);
93     return other;
94   }
95
96   /**
97    * Compare cursors for equality/inequality.
98    *
99    * Two cursors are equal if they are pointing to the same location in the
100    * same IOBuf chain.
101    */
102   bool operator==(const Derived& other) const {
103     return (offset_ == other.offset_) && (crtBuf_ == other.crtBuf_);
104   }
105   bool operator!=(const Derived& other) const {
106     return !operator==(other);
107   }
108
109   template <class T>
110   typename std::enable_if<std::is_arithmetic<T>::value, T>::type
111   read() {
112     T val;
113     pull(&val, sizeof(T));
114     return val;
115   }
116
117   template <class T>
118   T readBE() {
119     return Endian::big(read<T>());
120   }
121
122   template <class T>
123   T readLE() {
124     return Endian::little(read<T>());
125   }
126
127   /**
128    * Read a fixed-length string.
129    *
130    * The std::string-based APIs should probably be avoided unless you
131    * ultimately want the data to live in an std::string. You're better off
132    * using the pull() APIs to copy into a raw buffer otherwise.
133    */
134   std::string readFixedString(size_t len) {
135     std::string str;
136
137     str.reserve(len);
138     for (;;) {
139       // Fast path: it all fits in one buffer.
140       size_t available = length();
141       if (LIKELY(available >= len)) {
142         str.append(reinterpret_cast<const char*>(data()), len);
143         offset_ += len;
144         return str;
145       }
146
147       str.append(reinterpret_cast<const char*>(data()), available);
148       if (UNLIKELY(!tryAdvanceBuffer())) {
149         throw std::out_of_range("string underflow");
150       }
151       len -= available;
152     }
153   }
154
155   /**
156    * Read a string consisting of bytes until the given terminator character is
157    * seen. Raises an std::length_error if maxLength bytes have been processed
158    * before the terminator is seen.
159    *
160    * See comments in readFixedString() about when it's appropriate to use this
161    * vs. using pull().
162    */
163   std::string readTerminatedString(
164     char termChar = '\0',
165     size_t maxLength = std::numeric_limits<size_t>::max()) {
166     std::string str;
167
168     for (;;) {
169       const uint8_t* buf = data();
170       size_t buflen = length();
171
172       size_t i = 0;
173       while (i < buflen && buf[i] != termChar) {
174         ++i;
175
176         // Do this check after incrementing 'i', as even though we start at the
177         // 0 byte, it still represents a single character
178         if (str.length() + i >= maxLength) {
179           throw std::length_error("string overflow");
180         }
181       }
182
183       str.append(reinterpret_cast<const char*>(buf), i);
184       if (i < buflen) {
185         skip(i + 1);
186         return str;
187       }
188
189       skip(i);
190
191       if (UNLIKELY(!tryAdvanceBuffer())) {
192         throw std::out_of_range("string underflow");
193       }
194     }
195   }
196
197   explicit CursorBase(BufType* buf)
198     : crtBuf_(buf)
199     , offset_(0)
200     , buffer_(buf) {}
201
202   // Make all the templated classes friends for copy constructor.
203   template <class D, typename B> friend class CursorBase;
204
205   /*
206    * Copy constructor.
207    *
208    * This also allows constructing a CursorBase from other derived types.
209    * For instance, this allows constructing a Cursor from an RWPrivateCursor.
210    */
211   template <class OtherDerived, class OtherBuf>
212   explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor)
213     : crtBuf_(cursor.crtBuf_),
214       offset_(cursor.offset_),
215       buffer_(cursor.buffer_) {}
216
217   // reset cursor to point to a new buffer.
218   void reset(BufType* buf) {
219     crtBuf_ = buf;
220     buffer_ = buf;
221     offset_ = 0;
222   }
223
224   /**
225    * Return the available data in the current buffer.
226    * If you want to gather more data from the chain into a contiguous region
227    * (for hopefully zero-copy access), use gather() before peek().
228    */
229   std::pair<const uint8_t*, size_t> peek() {
230     // Ensure that we're pointing to valid data
231     size_t available = length();
232     while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
233       available = length();
234     }
235
236     return std::make_pair(data(), available);
237   }
238
239   /**
240    * Read one byte. Equivalent to pull(&byte, 1) but faster.
241    */
242   uint8_t pullByte() {
243     // fast path
244     if (LIKELY(length() >= 1)) {
245       uint8_t byte = *data();
246       offset_++;
247       return byte;
248     }
249
250     // slow path
251     uint8_t byte;
252     if (UNLIKELY(pullAtMost(&byte, 1) != 1)) {
253       throw std::out_of_range("underflow");
254     }
255     return byte;
256   }
257
258   void pull(void* buf, size_t len) {
259     if (UNLIKELY(pullAtMost(buf, len) != len)) {
260       throw std::out_of_range("underflow");
261     }
262   }
263
264   void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
265     if (UNLIKELY(cloneAtMost(buf, len) != len)) {
266       throw std::out_of_range("underflow");
267     }
268   }
269
270   void clone(folly::IOBuf& buf, size_t len) {
271     if (UNLIKELY(cloneAtMost(buf, len) != len)) {
272       throw std::out_of_range("underflow");
273     }
274   }
275
276   void skip(size_t len) {
277     if (UNLIKELY(skipAtMost(len) != len)) {
278       throw std::out_of_range("underflow");
279     }
280   }
281
282   size_t pullAtMost(void* buf, size_t len) {
283     uint8_t* p = reinterpret_cast<uint8_t*>(buf);
284     size_t copied = 0;
285     for (;;) {
286       // Fast path: it all fits in one buffer.
287       size_t available = length();
288       if (LIKELY(available >= len)) {
289         memcpy(p, data(), len);
290         offset_ += len;
291         return copied + len;
292       }
293
294       memcpy(p, data(), available);
295       copied += available;
296       if (UNLIKELY(!tryAdvanceBuffer())) {
297         return copied;
298       }
299       p += available;
300       len -= available;
301     }
302   }
303
304   size_t cloneAtMost(folly::IOBuf& buf, size_t len) {
305     buf = folly::IOBuf();
306
307     std::unique_ptr<folly::IOBuf> tmp;
308     size_t copied = 0;
309     for (int loopCount = 0; true; ++loopCount) {
310       // Fast path: it all fits in one buffer.
311       size_t available = length();
312       if (LIKELY(available >= len)) {
313         if (loopCount == 0) {
314           crtBuf_->cloneOneInto(buf);
315           buf.trimStart(offset_);
316           buf.trimEnd(buf.length() - len);
317         } else {
318           tmp = crtBuf_->cloneOne();
319           tmp->trimStart(offset_);
320           tmp->trimEnd(tmp->length() - len);
321           buf.prependChain(std::move(tmp));
322         }
323
324         offset_ += len;
325         return copied + len;
326       }
327
328
329       if (loopCount == 0) {
330         crtBuf_->cloneOneInto(buf);
331         buf.trimStart(offset_);
332       } else {
333         tmp = crtBuf_->cloneOne();
334         tmp->trimStart(offset_);
335         buf.prependChain(std::move(tmp));
336       }
337
338       copied += available;
339       if (UNLIKELY(!tryAdvanceBuffer())) {
340         return copied;
341       }
342       len -= available;
343     }
344   }
345
346   size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
347     if (!buf) {
348       buf = make_unique<folly::IOBuf>();
349     }
350
351     return cloneAtMost(*buf, len);
352   }
353
354   size_t skipAtMost(size_t len) {
355     size_t skipped = 0;
356     for (;;) {
357       // Fast path: it all fits in one buffer.
358       size_t available = length();
359       if (LIKELY(available >= len)) {
360         offset_ += len;
361         return skipped + len;
362       }
363
364       skipped += available;
365       if (UNLIKELY(!tryAdvanceBuffer())) {
366         return skipped;
367       }
368       len -= available;
369     }
370   }
371
372   /**
373    * Return the distance between two cursors.
374    */
375   size_t operator-(const CursorBase& other) const {
376     BufType *otherBuf = other.crtBuf_;
377     size_t len = 0;
378
379     if (otherBuf != crtBuf_) {
380       len += otherBuf->length() - other.offset_;
381
382       for (otherBuf = otherBuf->next();
383            otherBuf != crtBuf_ && otherBuf != other.buffer_;
384            otherBuf = otherBuf->next()) {
385         len += otherBuf->length();
386       }
387
388       if (otherBuf == other.buffer_) {
389         throw std::out_of_range("wrap-around");
390       }
391
392       len += offset_;
393     } else {
394       if (offset_ < other.offset_) {
395         throw std::out_of_range("underflow");
396       }
397
398       len += offset_ - other.offset_;
399     }
400
401     return len;
402   }
403
404   /**
405    * Return the distance from the given IOBuf to the this cursor.
406    */
407   size_t operator-(const BufType* buf) const {
408     size_t len = 0;
409
410     BufType *curBuf = buf;
411     while (curBuf != crtBuf_) {
412       len += curBuf->length();
413       curBuf = curBuf->next();
414       if (curBuf == buf || curBuf == buffer_) {
415         throw std::out_of_range("wrap-around");
416       }
417     }
418
419     len += offset_;
420     return len;
421   }
422
423  protected:
424   BufType* crtBuf_;
425   size_t offset_;
426
427   ~CursorBase(){}
428
429   BufType* head() {
430     return buffer_;
431   }
432
433   bool tryAdvanceBuffer() {
434     BufType* nextBuf = crtBuf_->next();
435     if (UNLIKELY(nextBuf == buffer_)) {
436       offset_ = crtBuf_->length();
437       return false;
438     }
439
440     offset_ = 0;
441     crtBuf_ = nextBuf;
442     static_cast<Derived*>(this)->advanceDone();
443     return true;
444   }
445
446  private:
447   void advanceDone() {
448   }
449
450   BufType* buffer_;
451 };
452
453 } //namespace detail
454
455 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
456  public:
457   explicit Cursor(const IOBuf* buf)
458     : detail::CursorBase<Cursor, const IOBuf>(buf) {}
459
460   template <class OtherDerived, class OtherBuf>
461   explicit Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
462     : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
463 };
464
465 namespace detail {
466
467 template <class Derived>
468 class Writable {
469  public:
470   template <class T>
471   typename std::enable_if<std::is_arithmetic<T>::value>::type
472   write(T value) {
473     const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
474     Derived* d = static_cast<Derived*>(this);
475     d->push(u8, sizeof(T));
476   }
477
478   template <class T>
479   void writeBE(T value) {
480     Derived* d = static_cast<Derived*>(this);
481     d->write(Endian::big(value));
482   }
483
484   template <class T>
485   void writeLE(T value) {
486     Derived* d = static_cast<Derived*>(this);
487     d->write(Endian::little(value));
488   }
489
490   void push(const uint8_t* buf, size_t len) {
491     Derived* d = static_cast<Derived*>(this);
492     if (d->pushAtMost(buf, len) != len) {
493       throw std::out_of_range("overflow");
494     }
495   }
496
497   void push(ByteRange buf) {
498     if (this->pushAtMost(buf) != buf.size()) {
499       throw std::out_of_range("overflow");
500     }
501   }
502
503   size_t pushAtMost(ByteRange buf) {
504     Derived* d = static_cast<Derived*>(this);
505     return d->pushAtMost(buf.data(), buf.size());
506   }
507
508   /**
509    * push len bytes of data from input cursor, data could be in an IOBuf chain.
510    * If input cursor contains less than len bytes, or this cursor has less than
511    * len bytes writable space, an out_of_range exception will be thrown.
512    */
513   void push(Cursor cursor, size_t len) {
514     if (this->pushAtMost(cursor, len) != len) {
515       throw std::out_of_range("overflow");
516     }
517   }
518
519   size_t pushAtMost(Cursor cursor, size_t len) {
520     size_t written = 0;
521     for(;;) {
522       auto currentBuffer = cursor.peek();
523       const uint8_t* crtData = currentBuffer.first;
524       size_t available = currentBuffer.second;
525       if (available == 0) {
526         // end of buffer chain
527         return written;
528       }
529       // all data is in current buffer
530       if (available >= len) {
531         this->push(crtData, len);
532         cursor.skip(len);
533         return written + len;
534       }
535
536       // write the whole current IOBuf
537       this->push(crtData, available);
538       cursor.skip(available);
539       written += available;
540       len -= available;
541     }
542   }
543 };
544
545 } // namespace detail
546
547 enum class CursorAccess {
548   PRIVATE,
549   UNSHARE
550 };
551
552 template <CursorAccess access>
553 class RWCursor
554   : public detail::CursorBase<RWCursor<access>, IOBuf>,
555     public detail::Writable<RWCursor<access>> {
556   friend class detail::CursorBase<RWCursor<access>, IOBuf>;
557  public:
558   explicit RWCursor(IOBuf* buf)
559     : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
560       maybeShared_(true) {}
561
562   template <class OtherDerived, class OtherBuf>
563   explicit RWCursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
564     : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
565       maybeShared_(true) {}
566   /**
567    * Gather at least n bytes contiguously into the current buffer,
568    * by coalescing subsequent buffers from the chain as necessary.
569    */
570   void gather(size_t n) {
571     // Forbid attempts to gather beyond the end of this IOBuf chain.
572     // Otherwise we could try to coalesce the head of the chain and end up
573     // accidentally freeing it, invalidating the pointer owned by external
574     // code.
575     //
576     // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
577     // checking.  We only have to perform an explicit check here when calling
578     // gather() on a non-head element.
579     if (this->crtBuf_ != this->head() && this->totalLength() < n) {
580       throw std::overflow_error("cannot gather() past the end of the chain");
581     }
582     this->crtBuf_->gather(this->offset_ + n);
583   }
584   void gatherAtMost(size_t n) {
585     size_t size = std::min(n, this->totalLength());
586     return this->crtBuf_->gather(this->offset_ + size);
587   }
588
589   using detail::Writable<RWCursor<access>>::pushAtMost;
590   size_t pushAtMost(const uint8_t* buf, size_t len) {
591     size_t copied = 0;
592     for (;;) {
593       // Fast path: the current buffer is big enough.
594       size_t available = this->length();
595       if (LIKELY(available >= len)) {
596         if (access == CursorAccess::UNSHARE) {
597           maybeUnshare();
598         }
599         memcpy(writableData(), buf, len);
600         this->offset_ += len;
601         return copied + len;
602       }
603
604       if (access == CursorAccess::UNSHARE) {
605         maybeUnshare();
606       }
607       memcpy(writableData(), buf, available);
608       copied += available;
609       if (UNLIKELY(!this->tryAdvanceBuffer())) {
610         return copied;
611       }
612       buf += available;
613       len -= available;
614     }
615   }
616
617   void insert(std::unique_ptr<folly::IOBuf> buf) {
618     folly::IOBuf* nextBuf;
619     if (this->offset_ == 0) {
620       // Can just prepend
621       nextBuf = this->crtBuf_;
622       this->crtBuf_->prependChain(std::move(buf));
623     } else {
624       std::unique_ptr<folly::IOBuf> remaining;
625       if (this->crtBuf_->length() - this->offset_ > 0) {
626         // Need to split current IOBuf in two.
627         remaining = this->crtBuf_->cloneOne();
628         remaining->trimStart(this->offset_);
629         nextBuf = remaining.get();
630         buf->prependChain(std::move(remaining));
631       } else {
632         // Can just append
633         nextBuf = this->crtBuf_->next();
634       }
635       this->crtBuf_->trimEnd(this->length());
636       this->crtBuf_->appendChain(std::move(buf));
637     }
638     // Jump past the new links
639     this->offset_ = 0;
640     this->crtBuf_ = nextBuf;
641   }
642
643   uint8_t* writableData() {
644     return this->crtBuf_->writableData() + this->offset_;
645   }
646
647  private:
648   void maybeUnshare() {
649     if (UNLIKELY(maybeShared_)) {
650       this->crtBuf_->unshareOne();
651       maybeShared_ = false;
652     }
653   }
654
655   void advanceDone() {
656     maybeShared_ = true;
657   }
658
659   bool maybeShared_;
660 };
661
662 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
663 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
664
665 /**
666  * Append to the end of a buffer chain, growing the chain (by allocating new
667  * buffers) in increments of at least growth bytes every time.  Won't grow
668  * (and push() and ensure() will throw) if growth == 0.
669  *
670  * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
671  * of chaining.
672  */
673 class Appender : public detail::Writable<Appender> {
674  public:
675   Appender(IOBuf* buf, uint64_t growth)
676     : buffer_(buf),
677       crtBuf_(buf->prev()),
678       growth_(growth) {
679   }
680
681   uint8_t* writableData() {
682     return crtBuf_->writableTail();
683   }
684
685   size_t length() const {
686     return crtBuf_->tailroom();
687   }
688
689   /**
690    * Mark n bytes (must be <= length()) as appended, as per the
691    * IOBuf::append() method.
692    */
693   void append(size_t n) {
694     crtBuf_->append(n);
695   }
696
697   /**
698    * Ensure at least n contiguous bytes available to write.
699    * Postcondition: length() >= n.
700    */
701   void ensure(uint64_t n) {
702     if (LIKELY(length() >= n)) {
703       return;
704     }
705
706     // Waste the rest of the current buffer and allocate a new one.
707     // Don't make it too small, either.
708     if (growth_ == 0) {
709       throw std::out_of_range("can't grow buffer chain");
710     }
711
712     n = std::max(n, growth_);
713     buffer_->prependChain(IOBuf::create(n));
714     crtBuf_ = buffer_->prev();
715   }
716
717   using detail::Writable<Appender>::pushAtMost;
718   size_t pushAtMost(const uint8_t* buf, size_t len) {
719     size_t copied = 0;
720     for (;;) {
721       // Fast path: it all fits in one buffer.
722       size_t available = length();
723       if (LIKELY(available >= len)) {
724         memcpy(writableData(), buf, len);
725         append(len);
726         return copied + len;
727       }
728
729       memcpy(writableData(), buf, available);
730       append(available);
731       copied += available;
732       if (UNLIKELY(!tryGrowChain())) {
733         return copied;
734       }
735       buf += available;
736       len -= available;
737     }
738   }
739
740   /*
741    * Append to the end of this buffer, using a printf() style
742    * format specifier.
743    *
744    * Note that folly/Format.h provides nicer and more type-safe mechanisms
745    * for formatting strings, which should generally be preferred over
746    * printf-style formatting.  Appender objects can be used directly as an
747    * output argument for Formatter objects.  For example:
748    *
749    *   Appender app(&iobuf);
750    *   format("{} {}", "hello", "world")(app);
751    *
752    * However, printf-style strings are still needed when dealing with existing
753    * third-party code in some cases.
754    *
755    * This will always add a nul-terminating character after the end
756    * of the output.  However, the buffer data length will only be updated to
757    * include the data itself.  The nul terminator will be the first byte in the
758    * buffer tailroom.
759    *
760    * This method may throw exceptions on error.
761    */
762   void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)
763     FOLLY_PRINTF_FORMAT_ATTR(2, 3);
764
765   void vprintf(const char* fmt, va_list ap);
766
767   /*
768    * Calling an Appender object with a StringPiece will append the string
769    * piece.  This allows Appender objects to be used directly with
770    * Formatter.
771    */
772   void operator()(StringPiece sp) {
773     push(ByteRange(sp));
774   }
775
776  private:
777   bool tryGrowChain() {
778     assert(crtBuf_->next() == buffer_);
779     if (growth_ == 0) {
780       return false;
781     }
782
783     buffer_->prependChain(IOBuf::create(growth_));
784     crtBuf_ = buffer_->prev();
785     return true;
786   }
787
788   IOBuf* buffer_;
789   IOBuf* crtBuf_;
790   uint64_t growth_;
791 };
792
793 class QueueAppender : public detail::Writable<QueueAppender> {
794  public:
795   /**
796    * Create an Appender that writes to a IOBufQueue.  When we allocate
797    * space in the queue, we grow no more than growth bytes at once
798    * (unless you call ensure() with a bigger value yourself).
799    */
800   QueueAppender(IOBufQueue* queue, uint64_t growth) {
801     reset(queue, growth);
802   }
803
804   void reset(IOBufQueue* queue, uint64_t growth) {
805     queue_ = queue;
806     growth_ = growth;
807   }
808
809   uint8_t* writableData() {
810     return static_cast<uint8_t*>(queue_->writableTail());
811   }
812
813   size_t length() const { return queue_->tailroom(); }
814
815   void append(size_t n) { queue_->postallocate(n); }
816
817   // Ensure at least n contiguous; can go above growth_, throws if
818   // not enough room.
819   void ensure(uint64_t n) { queue_->preallocate(n, growth_); }
820
821   template <class T>
822   typename std::enable_if<std::is_arithmetic<T>::value>::type
823   write(T value) {
824     // We can't fail.
825     auto p = queue_->preallocate(sizeof(T), growth_);
826     storeUnaligned(p.first, value);
827     queue_->postallocate(sizeof(T));
828   }
829
830   using detail::Writable<QueueAppender>::pushAtMost;
831   size_t pushAtMost(const uint8_t* buf, size_t len) {
832     size_t remaining = len;
833     while (remaining != 0) {
834       auto p = queue_->preallocate(std::min(remaining, growth_),
835                                    growth_,
836                                    remaining);
837       memcpy(p.first, buf, p.second);
838       queue_->postallocate(p.second);
839       buf += p.second;
840       remaining -= p.second;
841     }
842
843     return len;
844   }
845
846   void insert(std::unique_ptr<folly::IOBuf> buf) {
847     if (buf) {
848       queue_->append(std::move(buf), true);
849     }
850   }
851
852  private:
853   folly::IOBufQueue* queue_;
854   size_t growth_;
855 };
856
857 }}  // folly::io
858
859 #endif // FOLLY_CURSOR_H