X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Fio%2FCursor.h;h=ff9082b056020bab4a756d6dd652e6213e8bda16;hb=68f55ed0e97dea446cf66d429e8b46331cfb9f12;hp=3cf369e9ece27298ffa7eb2bf35a225bc83152a8;hpb=57e517a56bd048c902be1c1e1a0655316a5189a4;p=folly.git diff --git a/folly/io/Cursor.h b/folly/io/Cursor.h index 3cf369e9..ff9082b0 100644 --- a/folly/io/Cursor.h +++ b/folly/io/Cursor.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,13 @@ #include #include -#include -#include #include #include #include #include +#include +#include +#include /** * Cursor class for fast iteration over IOBuf chains. @@ -86,10 +87,10 @@ class CursorBase { /** * Return the remaining space available in the current IOBuf. * - * May return 0 if the cursor is at the end of an IOBuf. Use peek() instead - * if you want to avoid this. peek() will advance to the next non-empty - * IOBuf (up to the end of the chain) if the cursor is currently pointing at - * the end of a buffer. + * May return 0 if the cursor is at the end of an IOBuf. Use peekBytes() + * instead if you want to avoid this. peekBytes() will advance to the next + * non-empty IOBuf (up to the end of the chain) if the cursor is currently + * pointing at the end of a buffer. */ size_t length() const { return crtBuf_->length() - offset_; @@ -152,6 +153,17 @@ class CursorBase { return true; } + /** + * Advances the cursor to the end of the entire IOBuf chain. + */ + void advanceToEnd() { + offset_ = buffer_->prev()->length(); + if (crtBuf_ != buffer_->prev()) { + crtBuf_ = buffer_->prev(); + static_cast(this)->advanceDone(); + } + } + Derived& operator+=(size_t offset) { Derived* p = static_cast(this); p->skip(offset); @@ -163,6 +175,17 @@ class CursorBase { return other; } + Derived& operator-=(size_t offset) { + Derived* p = static_cast(this); + p->retreat(offset); + return *p; + } + Derived operator-(size_t offset) const { + Derived other(*this); + other.retreat(offset); + return other; + } + /** * Compare cursors for equality/inequality. * @@ -177,14 +200,36 @@ class CursorBase { } template - typename std::enable_if::value, T>::type read() { - T val; + typename std::enable_if::value, bool>::type tryRead( + T& val) { if (LIKELY(length() >= sizeof(T))) { val = loadUnaligned(data()); offset_ += sizeof(T); advanceBufferIfEmpty(); - } else { - pullSlow(&val, sizeof(T)); + return true; + } + return pullAtMostSlow(&val, sizeof(T)) == sizeof(T); + } + + template + bool tryReadBE(T& val) { + const bool result = tryRead(val); + val = Endian::big(val); + return result; + } + + template + bool tryReadLE(T& val) { + const bool result = tryRead(val); + val = Endian::little(val); + return result; + } + + template + T read() { + T val{}; + if (!tryRead(val)) { + std::__throw_out_of_range("underflow"); } return val; } @@ -229,34 +274,36 @@ class CursorBase { */ std::string readTerminatedString( char termChar = '\0', - size_t maxLength = std::numeric_limits::max()) { - std::string str; - - while (!isAtEnd()) { - const uint8_t* buf = data(); - size_t buflen = length(); + size_t maxLength = std::numeric_limits::max()); - size_t i = 0; - while (i < buflen && buf[i] != termChar) { - ++i; - - // Do this check after incrementing 'i', as even though we start at the - // 0 byte, it still represents a single character - if (str.length() + i >= maxLength) { - throw std::length_error("string overflow"); - } - } + /* + * Read all bytes until the specified predicate returns true. + * + * The predicate will be called on each byte in turn, until it returns false + * or until the end of the IOBuf chain is reached. + * + * Returns the result as a string. + */ + template + std::string readWhile(const Predicate& predicate); - str.append(reinterpret_cast(buf), i); - if (i < buflen) { - skip(i + 1); - return str; - } + /* + * Read all bytes until the specified predicate returns true. + * + * This is a more generic version of readWhile() takes an arbitrary Output + * object, and calls Output::append() with each chunk of matching data. + */ + template + void readWhile(const Predicate& predicate, Output& out); - skip(i); - } - throw std::out_of_range("terminator not found"); - } + /* + * Skip all bytes until the specified predicate returns true. + * + * The predicate will be called on each byte in turn, until it returns false + * or until the end of the IOBuf chain is reached. + */ + template + void skipWhile(const Predicate& predicate); size_t skipAtMost(size_t len) { if (LIKELY(length() >= len)) { @@ -276,6 +323,22 @@ class CursorBase { } } + size_t retreatAtMost(size_t len) { + if (len <= offset_) { + offset_ -= len; + return len; + } + return retreatAtMostSlow(len); + } + + void retreat(size_t len) { + if (len <= offset_) { + offset_ -= len; + } else { + retreatSlow(len); + } + } + size_t pullAtMost(void* buf, size_t len) { // Fast path: it all fits in one buffer. if (LIKELY(length() >= len)) { @@ -300,26 +363,37 @@ class CursorBase { /** * Return the available data in the current buffer. * If you want to gather more data from the chain into a contiguous region - * (for hopefully zero-copy access), use gather() before peek(). + * (for hopefully zero-copy access), use gather() before peekBytes(). */ - std::pair peek() { + ByteRange peekBytes() { // Ensure that we're pointing to valid data size_t available = length(); while (UNLIKELY(available == 0 && tryAdvanceBuffer())) { available = length(); } - return std::make_pair(data(), available); + return ByteRange{data(), available}; + } + + /** + * Alternate version of peekBytes() that returns a std::pair + * instead of a ByteRange. (This method pre-dates ByteRange.) + * + * This function will eventually be deprecated. + */ + std::pair peek() { + auto bytes = peekBytes(); + return std::make_pair(bytes.data(), bytes.size()); } void clone(std::unique_ptr& buf, size_t len) { if (UNLIKELY(cloneAtMost(buf, len) != len)) { - throw std::out_of_range("underflow"); + std::__throw_out_of_range("underflow"); } } void clone(folly::IOBuf& buf, size_t len) { if (UNLIKELY(cloneAtMost(buf, len) != len)) { - throw std::out_of_range("underflow"); + std::__throw_out_of_range("underflow"); } } @@ -365,7 +439,7 @@ class CursorBase { size_t cloneAtMost(std::unique_ptr& buf, size_t len) { if (!buf) { - buf = make_unique(); + buf = std::make_unique(); } return cloneAtMost(*buf, len); } @@ -387,13 +461,13 @@ class CursorBase { } if (otherBuf == other.buffer_) { - throw std::out_of_range("wrap-around"); + std::__throw_out_of_range("wrap-around"); } len += offset_; } else { if (offset_ < other.offset_) { - throw std::out_of_range("underflow"); + std::__throw_out_of_range("underflow"); } len += offset_ - other.offset_; @@ -408,12 +482,12 @@ class CursorBase { size_t operator-(const BufType* buf) const { size_t len = 0; - BufType *curBuf = buf; + const BufType* curBuf = buf; while (curBuf != crtBuf_) { len += curBuf->length(); curBuf = curBuf->next(); if (curBuf == buf || curBuf == buffer_) { - throw std::out_of_range("wrap-around"); + std::__throw_out_of_range("wrap-around"); } } @@ -441,6 +515,17 @@ class CursorBase { return true; } + bool tryRetreatBuffer() { + if (UNLIKELY(crtBuf_ == buffer_)) { + offset_ = 0; + return false; + } + crtBuf_ = crtBuf_->prev(); + offset_ = crtBuf_->length(); + static_cast(this)->advanceDone(); + return true; + } + void advanceBufferIfEmpty() { if (length() == 0) { tryAdvanceBuffer(); @@ -455,7 +540,7 @@ class CursorBase { for (size_t available; (available = length()) < len; ) { str->append(reinterpret_cast(data()), available); if (UNLIKELY(!tryAdvanceBuffer())) { - throw std::out_of_range("string underflow"); + std::__throw_out_of_range("string underflow"); } len -= available; } @@ -484,7 +569,7 @@ class CursorBase { void pullSlow(void* buf, size_t len) { if (UNLIKELY(pullAtMostSlow(buf, len) != len)) { - throw std::out_of_range("underflow"); + std::__throw_out_of_range("underflow"); } } @@ -504,7 +589,26 @@ class CursorBase { void skipSlow(size_t len) { if (UNLIKELY(skipAtMostSlow(len) != len)) { - throw std::out_of_range("underflow"); + std::__throw_out_of_range("underflow"); + } + } + + size_t retreatAtMostSlow(size_t len) { + size_t retreated = 0; + for (size_t available; (available = offset_) < len;) { + retreated += available; + if (UNLIKELY(!tryRetreatBuffer())) { + return retreated; + } + len -= available; + } + offset_ -= len; + return retreated + len; + } + + void retreatSlow(size_t len) { + if (UNLIKELY(retreatAtMostSlow(len) != len)) { + std::__throw_out_of_range("underflow"); } } @@ -554,13 +658,13 @@ class Writable { void push(const uint8_t* buf, size_t len) { Derived* d = static_cast(this); if (d->pushAtMost(buf, len) != len) { - throw std::out_of_range("overflow"); + std::__throw_out_of_range("overflow"); } } void push(ByteRange buf) { if (this->pushAtMost(buf) != buf.size()) { - throw std::out_of_range("overflow"); + std::__throw_out_of_range("overflow"); } } @@ -576,16 +680,16 @@ class Writable { */ void push(Cursor cursor, size_t len) { if (this->pushAtMost(cursor, len) != len) { - throw std::out_of_range("overflow"); + std::__throw_out_of_range("overflow"); } } size_t pushAtMost(Cursor cursor, size_t len) { size_t written = 0; for(;;) { - auto currentBuffer = cursor.peek(); - const uint8_t* crtData = currentBuffer.first; - size_t available = currentBuffer.second; + auto currentBuffer = cursor.peekBytes(); + const uint8_t* crtData = currentBuffer.data(); + size_t available = currentBuffer.size(); if (available == 0) { // end of buffer chain return written; @@ -770,7 +874,7 @@ class Appender : public detail::Writable { // Waste the rest of the current buffer and allocate a new one. // Don't make it too small, either. if (growth_ == 0) { - throw std::out_of_range("can't grow buffer chain"); + std::__throw_out_of_range("can't grow buffer chain"); } n = std::max(n, growth_); @@ -893,7 +997,15 @@ class QueueAppender : public detail::Writable { using detail::Writable::pushAtMost; size_t pushAtMost(const uint8_t* buf, size_t len) { - size_t remaining = len; + // Fill the current buffer + const size_t copyLength = std::min(len, length()); + if (copyLength != 0) { + memcpy(writableData(), buf, copyLength); + append(copyLength); + buf += copyLength; + } + // Allocate more buffers as necessary + size_t remaining = len - copyLength; while (remaining != 0) { auto p = queue_->preallocate(std::min(remaining, growth_), growth_, @@ -913,9 +1025,15 @@ class QueueAppender : public detail::Writable { } } + void insert(const folly::IOBuf& buf) { + insert(buf.clone()); + } + private: folly::IOBufQueue* queue_; size_t growth_; }; }} // folly::io + +#include