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