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