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