2 * Copyright 2013 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_CURSOR_H
18 #define FOLLY_CURSOR_H
23 #include <type_traits>
26 #include "folly/Bits.h"
27 #include "folly/io/IOBuf.h"
28 #include "folly/Likely.h"
31 * Cursor class for fast iteration over IOBuf chains.
33 * Cursor - Read-only access
35 * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
36 * RWUnshareCursor - Read-write access, calls unshare on write (COW)
37 * Appender - Write access, assumes private access to IOBuf chian
39 * Note that RW cursors write in the preallocated part of buffers (that is,
40 * between the buffer's data() and tail()), while Appenders append to the end
41 * of the buffer (between the buffer's tail() and bufferEnd()). Appenders
42 * automatically adjust the buffer pointers, so you may only use one
43 * Appender with a buffer chain; for this reason, Appenders assume private
44 * access to the buffer (you need to call unshare() yourself if necessary).
46 namespace folly { namespace io {
49 template <class Derived, typename BufType>
52 const uint8_t* data() const {
53 return crtBuf_->data() + offset_;
56 // Space available in the current IOBuf. May be 0; use peek() instead which
57 // will always point to a non-empty chunk of data or at the end of the
59 size_t length() const {
60 return crtBuf_->length() - offset_;
63 Derived& operator+=(size_t offset) {
64 Derived* p = static_cast<Derived*>(this);
70 typename std::enable_if<std::is_integral<T>::value, T>::type
73 pull(&val, sizeof(T));
79 return Endian::big(read<T>());
84 return Endian::little(read<T>());
87 explicit CursorBase(BufType* buf)
92 // Make all the templated classes friends for copy constructor.
93 template <class D, typename B> friend class CursorBase;
96 explicit CursorBase(const T& cursor) {
97 crtBuf_ = cursor.crtBuf_;
98 offset_ = cursor.offset_;
99 buffer_ = cursor.buffer_;
102 // reset cursor to point to a new buffer.
103 void reset(BufType* buf) {
110 * Return the available data in the current buffer.
111 * If you want to gather more data from the chain into a contiguous region
112 * (for hopefully zero-copy access), use gather() before peek().
114 std::pair<const uint8_t*, size_t> peek() {
115 // Ensure that we're pointing to valid data
116 size_t available = length();
117 while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
118 available = length();
121 return std::make_pair(data(), available);
124 void pull(void* buf, size_t length) {
125 if (UNLIKELY(pullAtMost(buf, length) != length)) {
126 throw std::out_of_range("underflow");
130 void clone(std::unique_ptr<folly::IOBuf>& buf, size_t length) {
131 if (UNLIKELY(cloneAtMost(buf, length) != length)) {
132 throw std::out_of_range("underflow");
136 void skip(size_t length) {
137 if (UNLIKELY(skipAtMost(length) != length)) {
138 throw std::out_of_range("underflow");
142 size_t pullAtMost(void* buf, size_t len) {
143 uint8_t* p = reinterpret_cast<uint8_t*>(buf);
146 // Fast path: it all fits in one buffer.
147 size_t available = length();
148 if (LIKELY(available >= len)) {
149 memcpy(p, data(), len);
154 memcpy(p, data(), available);
156 if (UNLIKELY(!tryAdvanceBuffer())) {
164 size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
167 std::unique_ptr<folly::IOBuf> tmp;
170 // Fast path: it all fits in one buffer.
171 size_t available = length();
172 if (LIKELY(available >= len)) {
173 tmp = crtBuf_->cloneOne();
174 tmp->trimStart(offset_);
175 tmp->trimEnd(tmp->length() - len);
178 buf = std::move(tmp);
180 buf->prependChain(std::move(tmp));
185 tmp = crtBuf_->cloneOne();
186 tmp->trimStart(offset_);
188 buf = std::move(tmp);
190 buf->prependChain(std::move(tmp));
194 if (UNLIKELY(!tryAdvanceBuffer())) {
201 size_t skipAtMost(size_t len) {
204 // Fast path: it all fits in one buffer.
205 size_t available = length();
206 if (LIKELY(available >= len)) {
208 return skipped + len;
211 skipped += available;
212 if (UNLIKELY(!tryAdvanceBuffer())) {
225 bool tryAdvanceBuffer() {
226 BufType* nextBuf = crtBuf_->next();
227 if (UNLIKELY(nextBuf == buffer_)) {
228 offset_ = crtBuf_->length();
234 static_cast<Derived*>(this)->advanceDone();
245 template <class Derived>
249 typename std::enable_if<std::is_integral<T>::value>::type
251 const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
256 void writeBE(T value) {
257 write(Endian::big(value));
261 void writeLE(T value) {
262 write(Endian::little(value));
265 void push(const uint8_t* buf, size_t len) {
266 Derived* d = static_cast<Derived*>(this);
267 if (d->pushAtMost(buf, len) != len) {
268 throw std::out_of_range("overflow");
273 } // namespace detail
275 class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
277 explicit Cursor(const IOBuf* buf)
278 : detail::CursorBase<Cursor, const IOBuf>(buf) {}
280 template <class CursorType>
281 explicit Cursor(CursorType& cursor)
282 : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
285 enum class CursorAccess {
290 template <CursorAccess access>
292 : public detail::CursorBase<RWCursor<access>, IOBuf>,
293 public detail::Writable<RWCursor<access>> {
294 friend class detail::CursorBase<RWCursor<access>, IOBuf>;
296 explicit RWCursor(IOBuf* buf)
297 : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
298 maybeShared_(true) {}
300 template <class CursorType>
301 explicit RWCursor(CursorType& cursor)
302 : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
303 maybeShared_(true) {}
305 * Gather at least n bytes contiguously into the current buffer,
306 * by coalescing subsequent buffers from the chain as necessary.
308 void gather(size_t n) {
309 this->crtBuf_->gather(this->offset_ + n);
312 size_t pushAtMost(const uint8_t* buf, size_t len) {
315 // Fast path: the current buffer is big enough.
316 size_t available = this->length();
317 if (LIKELY(available >= len)) {
318 if (access == CursorAccess::UNSHARE) {
321 memcpy(writableData(), buf, len);
322 this->offset_ += len;
326 if (access == CursorAccess::UNSHARE) {
329 memcpy(writableData(), buf, available);
331 if (UNLIKELY(!this->tryAdvanceBuffer())) {
339 void insert(std::unique_ptr<folly::IOBuf> buf) {
340 folly::IOBuf* nextBuf;
341 if (this->offset_ == 0) {
344 this->crtBuf_->prependChain(std::move(buf));
346 std::unique_ptr<folly::IOBuf> remaining;
347 if (this->crtBuf_->length() - this->offset_ > 0) {
348 // Need to split current IOBuf in two.
349 remaining = this->crtBuf_->cloneOne();
350 remaining->trimStart(this->offset_);
351 nextBuf = remaining.get();
352 buf->prependChain(std::move(remaining));
355 nextBuf = this->crtBuf_->next();
357 this->crtBuf_->trimEnd(this->length());
358 this->crtBuf_->appendChain(std::move(buf));
360 // Jump past the new links
362 this->crtBuf_ = nextBuf;
365 uint8_t* writableData() {
366 return this->crtBuf_->writableData() + this->offset_;
370 void maybeUnshare() {
371 if (UNLIKELY(maybeShared_)) {
372 this->crtBuf_->unshareOne();
373 maybeShared_ = false;
384 typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
385 typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
388 * Append to the end of a buffer chain, growing the chain (by allocating new
389 * buffers) in increments of at least growth bytes every time. Won't grow
390 * (and push() and ensure() will throw) if growth == 0.
392 * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
395 class Appender : public detail::Writable<Appender> {
397 Appender(IOBuf* buf, uint32_t growth)
399 crtBuf_(buf->prev()),
403 uint8_t* writableData() {
404 return crtBuf_->writableTail();
407 size_t length() const {
408 return crtBuf_->tailroom();
412 * Mark n bytes (must be <= length()) as appended, as per the
413 * IOBuf::append() method.
415 void append(size_t n) {
420 * Ensure at least n contiguous bytes available to write.
421 * Postcondition: length() >= n.
423 void ensure(uint32_t n) {
424 if (LIKELY(length() >= n)) {
428 // Waste the rest of the current buffer and allocate a new one.
429 // Don't make it too small, either.
431 throw std::out_of_range("can't grow buffer chain");
434 n = std::max(n, growth_);
435 buffer_->prependChain(IOBuf::create(n));
436 crtBuf_ = buffer_->prev();
439 size_t pushAtMost(const uint8_t* buf, size_t len) {
442 // Fast path: it all fits in one buffer.
443 size_t available = length();
444 if (LIKELY(available >= len)) {
445 memcpy(writableData(), buf, len);
450 memcpy(writableData(), buf, available);
453 if (UNLIKELY(!tryGrowChain())) {
462 bool tryGrowChain() {
463 assert(crtBuf_->next() == buffer_);
468 buffer_->prependChain(IOBuf::create(growth_));
469 crtBuf_ = buffer_->prev();
480 #endif // FOLLY_CURSOR_H