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