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