template <class OtherDerived, class OtherBuf>
explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor)
: crtBuf_(cursor.crtBuf_),
+ buffer_(cursor.buffer_),
crtBegin_(cursor.crtBegin_),
crtEnd_(cursor.crtEnd_),
crtPos_(cursor.crtPos_),
- buffer_(cursor.buffer_) {}
+ absolutePos_(cursor.absolutePos_) {}
/**
* Reset cursor to point to a new buffer.
void reset(BufType* buf) {
crtBuf_ = buf;
buffer_ = buf;
+ absolutePos_ = 0;
if (crtBuf_) {
crtPos_ = crtBegin_ = crtBuf_->data();
crtEnd_ = crtBuf_->tail();
}
}
+ /**
+ * Get the current Cursor position relative to the head of IOBuf chain.
+ */
+ size_t getCurrentPosition() const {
+ return (crtPos_ - crtBegin_) + absolutePos_;
+ }
+
const uint8_t* data() const {
return crtPos_;
}
* Advances the cursor to the end of the entire IOBuf chain.
*/
void advanceToEnd() {
- crtBegin_ = buffer_->prev()->data();
- crtPos_ = crtEnd_ = buffer_->prev()->tail();
- if (crtBuf_ != buffer_->prev()) {
- crtBuf_ = buffer_->prev();
+ // Simple case, we're already in the last IOBuf.
+ if (crtBuf_ == buffer_->prev()) {
+ crtPos_ = crtEnd_;
+ return;
+ }
+
+ auto* nextBuf = crtBuf_->next();
+ while (nextBuf != buffer_) {
+ absolutePos_ += crtEnd_ - crtBegin_;
+
+ crtBuf_ = nextBuf;
+ nextBuf = crtBuf_->next();
+ crtBegin_ = crtBuf_->data();
+ crtPos_ = crtEnd_ = crtBuf_->tail();
+
static_cast<Derived*>(this)->advanceDone();
}
}
return false;
}
+ absolutePos_ += crtEnd_ - crtBegin_;
crtBuf_ = nextBuf;
crtPos_ = crtBegin_ = crtBuf_->data();
crtEnd_ = crtBuf_->tail();
crtBuf_ = crtBuf_->prev();
crtBegin_ = crtBuf_->data();
crtPos_ = crtEnd_ = crtBuf_->tail();
+ absolutePos_ -= crtEnd_ - crtBegin_;
static_cast<Derived*>(this)->advanceDone();
return true;
}
}
BufType* crtBuf_;
+ BufType* buffer_;
const uint8_t* crtBegin_{nullptr};
const uint8_t* crtEnd_{nullptr};
const uint8_t* crtPos_{nullptr};
+ size_t absolutePos_{0};
private:
template <class T>
void advanceDone() {
}
-
- BufType* buffer_;
};
} // namespace detail
}
void insert(std::unique_ptr<folly::IOBuf> buf) {
- folly::IOBuf* nextBuf;
- if (this->crtPos_ == this->crtBegin_) {
+ this->absolutePos_ += buf->computeChainDataLength();
+ if (this->crtPos_ == this->crtBegin_ && this->crtBuf_ != this->buffer_) {
// Can just prepend
- nextBuf = this->crtBuf_;
this->crtBuf_->prependChain(std::move(buf));
} else {
+ IOBuf* nextBuf;
std::unique_ptr<folly::IOBuf> remaining;
if (this->crtPos_ != this->crtEnd_) {
// Need to split current IOBuf in two.
nextBuf = this->crtBuf_->next();
}
this->crtBuf_->trimEnd(this->length());
+ this->absolutePos_ += this->crtPos_ - this->crtBegin_;
this->crtBuf_->appendChain(std::move(buf));
// Jump past the new links
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include <folly/io/IOBuf.h>
#include <folly/Format.h>
cursor.insert(std::move(cloned));
cursor.insert(folly::IOBuf::create(0));
+ EXPECT_EQ(4, cursor.getCurrentPosition());
EXPECT_EQ(7, iobuf1->countChainElements());
EXPECT_EQ(14, iobuf1->computeChainDataLength());
// Check that nextBuf got set correctly to the buffer with 1 byte left
cursor.skip(1);
cursor.insert(std::move(cloned));
+ EXPECT_EQ(2, cursor.getCurrentPosition());
EXPECT_EQ(8, iobuf1->countChainElements());
EXPECT_EQ(15, iobuf1->computeChainDataLength());
// Check that nextBuf got set correctly
cursor.read<uint8_t>();
}
{
- // Check that inserting at the beginning doesn't create empty buf
+ // Check that inserting at the beginning of a chunk (except first one)
+ // doesn't create empty buf
RWPrivateCursor cursor(iobuf1.get());
Cursor(iobuf1.get()).clone(cloned, 1);
EXPECT_EQ(1, cloned->countChainElements());
EXPECT_EQ(1, cloned->computeChainDataLength());
+ cursor.skip(1);
+
cursor.insert(std::move(cloned));
+ EXPECT_EQ(2, cursor.getCurrentPosition());
+ EXPECT_EQ(14, cursor.totalLength());
EXPECT_EQ(9, iobuf1->countChainElements());
EXPECT_EQ(16, iobuf1->computeChainDataLength());
// Check that nextBuf got set correctly
cursor.read<uint8_t>();
}
+ {
+ // Check that inserting at the beginning of a chain DOES keep an empty
+ // buffer.
+ RWPrivateCursor cursor(iobuf1.get());
+ Cursor(iobuf1.get()).clone(cloned, 1);
+ EXPECT_EQ(1, cloned->countChainElements());
+ EXPECT_EQ(1, cloned->computeChainDataLength());
+
+ cursor.insert(std::move(cloned));
+ EXPECT_EQ(1, cursor.getCurrentPosition());
+ EXPECT_EQ(16, cursor.totalLength());
+ EXPECT_EQ(11, iobuf1->countChainElements());
+ EXPECT_EQ(17, iobuf1->computeChainDataLength());
+ // Check that nextBuf got set correctly
+ cursor.read<uint8_t>();
+ }
}
TEST(IOBuf, cloneWithEmptyBufAtStart) {
app.push(emptyBytes);
EXPECT_EQ(0, buf.computeChainDataLength());
}
+
+TEST(IOBuf, positionTracking) {
+ unique_ptr<IOBuf> iobuf1(IOBuf::create(6));
+ iobuf1->append(6);
+ unique_ptr<IOBuf> iobuf2(IOBuf::create(24));
+ iobuf2->append(24);
+ iobuf1->prependChain(std::move(iobuf2));
+
+ Cursor cursor(iobuf1.get());
+
+ EXPECT_EQ(0, cursor.getCurrentPosition());
+ EXPECT_EQ(6, cursor.length());
+
+ cursor.skip(3);
+ EXPECT_EQ(3, cursor.getCurrentPosition());
+ EXPECT_EQ(3, cursor.length());
+
+ // Test that we properly handle advancing to the next chunk.
+ cursor.skip(4);
+ EXPECT_EQ(7, cursor.getCurrentPosition());
+ EXPECT_EQ(23, cursor.length());
+
+ // Test that we properly handle doing to the previous chunk.
+ cursor.retreat(2);
+ EXPECT_EQ(5, cursor.getCurrentPosition());
+ EXPECT_EQ(1, cursor.length());
+
+ // Test that we properly handle advanceToEnd
+ cursor.advanceToEnd();
+ EXPECT_EQ(30, cursor.getCurrentPosition());
+ EXPECT_EQ(0, cursor.totalLength());
+
+ // Reset to 0.
+ cursor.reset(iobuf1.get());
+ EXPECT_EQ(0, cursor.getCurrentPosition());
+ EXPECT_EQ(30, cursor.totalLength());
+}