2017
[folly.git] / folly / io / IOBufQueue.cpp
index a34021eeb505f4f101c6146c94bd4ecafe144afe..6e4715d7d3af94c00135807488194de2e1136afb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "folly/io/IOBufQueue.h"
+#include <folly/io/IOBufQueue.h>
 
 #include <string.h>
 
@@ -29,7 +29,7 @@ namespace {
 using folly::IOBuf;
 
 const size_t MIN_ALLOC_SIZE = 2000;
-const size_t MAX_ALLOC_SIZE = 8000; // Must fit within a uint32_t
+const size_t MAX_ALLOC_SIZE = 8000;
 const size_t MAX_PACK_COPY = 4096;
 
 /**
@@ -37,7 +37,7 @@ const size_t MAX_PACK_COPY = 4096;
  */
 void
 appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) {
-  if (dst == NULL) {
+  if (dst == nullptr) {
     dst = std::move(src);
   } else {
     IOBuf* tail = dst->prev();
@@ -46,7 +46,7 @@ appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) {
       // reduce wastage (the tail's tailroom and the head's headroom) when
       // joining two IOBufQueues together.
       size_t copyRemaining = MAX_PACK_COPY;
-      uint32_t n;
+      uint64_t n;
       while (src &&
              (n = src->length()) < copyRemaining &&
              n < tail->tailroom()) {
@@ -71,7 +71,7 @@ IOBufQueue::IOBufQueue(const Options& options)
     chainLength_(0) {
 }
 
-IOBufQueue::IOBufQueue(IOBufQueue&& other)
+IOBufQueue::IOBufQueue(IOBufQueue&& other) noexcept
   : options_(other.options_),
     chainLength_(other.chainLength_),
     head_(std::move(other.head_)) {
@@ -88,7 +88,7 @@ IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) {
   return *this;
 }
 
-std::pair<void*, uint32_t>
+std::pair<void*, uint64_t>
 IOBufQueue::headroom() {
   if (head_) {
     return std::make_pair(head_->writableBuffer(), head_->headroom());
@@ -98,19 +98,17 @@ IOBufQueue::headroom() {
 }
 
 void
-IOBufQueue::markPrepended(uint32_t n) {
+IOBufQueue::markPrepended(uint64_t n) {
   if (n == 0) {
     return;
   }
   assert(head_);
   head_->prepend(n);
-  if (options_.cacheChainLength) {
-    chainLength_ += n;
-  }
+  chainLength_ += n;
 }
 
 void
-IOBufQueue::prepend(const void* buf, uint32_t n) {
+IOBufQueue::prepend(const void* buf, uint64_t n) {
   auto p = headroom();
   if (n > p.second) {
     throw std::overflow_error("Not enough room to prepend");
@@ -150,27 +148,25 @@ void
 IOBufQueue::append(const void* buf, size_t len) {
   auto src = static_cast<const uint8_t*>(buf);
   while (len != 0) {
-    if ((head_ == NULL) || head_->prev()->isSharedOne() ||
+    if ((head_ == nullptr) || head_->prev()->isSharedOne() ||
         (head_->prev()->tailroom() == 0)) {
-      appendToChain(head_, std::move(
+      appendToChain(head_,
           IOBuf::create(std::max(MIN_ALLOC_SIZE,
-              std::min(len, MAX_ALLOC_SIZE)))),
+              std::min(len, MAX_ALLOC_SIZE))),
           false);
     }
     IOBuf* last = head_->prev();
-    uint32_t copyLen = std::min(len, (size_t)last->tailroom());
+    uint64_t copyLen = std::min(len, (size_t)last->tailroom());
     memcpy(last->writableTail(), src, copyLen);
     src += copyLen;
     last->append(copyLen);
-    if (options_.cacheChainLength) {
-      chainLength_ += copyLen;
-    }
+    chainLength_ += copyLen;
     len -= copyLen;
   }
 }
 
 void
-IOBufQueue::wrapBuffer(const void* buf, size_t len, uint32_t blockSize) {
+IOBufQueue::wrapBuffer(const void* buf, size_t len, uint64_t blockSize) {
   auto src = static_cast<const uint8_t*>(buf);
   while (len != 0) {
     size_t n = std::min(len, size_t(blockSize));
@@ -180,19 +176,9 @@ IOBufQueue::wrapBuffer(const void* buf, size_t len, uint32_t blockSize) {
   }
 }
 
-pair<void*,uint32_t>
-IOBufQueue::preallocate(uint32_t min, uint32_t newAllocationSize,
-                        uint32_t max) {
-  if (head_ != NULL) {
-    // If there's enough space left over at the end of the queue, use that.
-    IOBuf* last = head_->prev();
-    if (!last->isSharedOne()) {
-      uint32_t avail = last->tailroom();
-      if (avail >= min) {
-        return make_pair(last->writableTail(), std::min(max, avail));
-      }
-    }
-  }
+pair<void*,uint64_t>
+IOBufQueue::preallocateSlow(uint64_t min, uint64_t newAllocationSize,
+                            uint64_t max) {
   // Allocate a new buffer of the requested max size.
   unique_ptr<IOBuf> newBuf(IOBuf::create(std::max(min, newAllocationSize)));
   appendToChain(head_, std::move(newBuf), false);
@@ -201,26 +187,19 @@ IOBufQueue::preallocate(uint32_t min, uint32_t newAllocationSize,
                    std::min(max, last->tailroom()));
 }
 
-void
-IOBufQueue::postallocate(uint32_t n) {
-  head_->prev()->append(n);
-  if (options_.cacheChainLength) {
-    chainLength_ += n;
-  }
-}
-
-unique_ptr<IOBuf>
-IOBufQueue::split(size_t n) {
+unique_ptr<IOBuf> IOBufQueue::split(size_t n, bool throwOnUnderflow) {
   unique_ptr<IOBuf> result;
   while (n != 0) {
-    if (head_ == NULL) {
-      throw std::underflow_error(
-          "Attempt to remove more bytes than are present in IOBufQueue");
+    if (head_ == nullptr) {
+      if (throwOnUnderflow) {
+        throw std::underflow_error(
+            "Attempt to remove more bytes than are present in IOBufQueue");
+      } else {
+        break;
+      }
     } else if (head_->length() <= n) {
       n -= head_->length();
-      if (options_.cacheChainLength) {
-        chainLength_ -= head_->length();
-      }
+      chainLength_ -= head_->length();
       unique_ptr<IOBuf> remainder = head_->pop();
       appendToChain(result, std::move(head_), false);
       head_ = std::move(remainder);
@@ -229,13 +208,11 @@ IOBufQueue::split(size_t n) {
       clone->trimEnd(clone->length() - n);
       appendToChain(result, std::move(clone), false);
       head_->trimStart(n);
-      if (options_.cacheChainLength) {
-        chainLength_ -= n;
-      }
+      chainLength_ -= n;
       break;
     }
   }
-  return std::move(result);
+  return result;
 }
 
 void IOBufQueue::trimStart(size_t amount) {
@@ -246,15 +223,11 @@ void IOBufQueue::trimStart(size_t amount) {
     }
     if (head_->length() > amount) {
       head_->trimStart(amount);
-      if (options_.cacheChainLength) {
-        chainLength_ -= amount;
-      }
+      chainLength_ -= amount;
       break;
     }
     amount -= head_->length();
-    if (options_.cacheChainLength) {
-      chainLength_ -= head_->length();
-    }
+    chainLength_ -= head_->length();
     head_ = head_->pop();
   }
 }
@@ -267,15 +240,11 @@ void IOBufQueue::trimEnd(size_t amount) {
     }
     if (head_->prev()->length() > amount) {
       head_->prev()->trimEnd(amount);
-      if (options_.cacheChainLength) {
-        chainLength_ -= amount;
-      }
+      chainLength_ -= amount;
       break;
     }
     amount -= head_->prev()->length();
-    if (options_.cacheChainLength) {
-      chainLength_ -= head_->prev()->length();
-    }
+    chainLength_ -= head_->prev()->length();
 
     if (head_->isChained()) {
       head_->prev()->unlink();
@@ -285,4 +254,45 @@ void IOBufQueue::trimEnd(size_t amount) {
   }
 }
 
+std::unique_ptr<folly::IOBuf> IOBufQueue::pop_front() {
+  if (!head_) {
+    return nullptr;
+  }
+  chainLength_ -= head_->length();
+  std::unique_ptr<folly::IOBuf> retBuf = std::move(head_);
+  head_ = retBuf->pop();
+  return retBuf;
+}
+
+void IOBufQueue::clear() {
+  if (!head_) {
+    return;
+  }
+  IOBuf* buf = head_.get();
+  do {
+    buf->clear();
+    buf = buf->next();
+  } while (buf != head_.get());
+  chainLength_ = 0;
+}
+
+void IOBufQueue::appendToString(std::string& out) const {
+  if (!head_) {
+    return;
+  }
+  auto len =
+    options_.cacheChainLength ? chainLength_ : head_->computeChainDataLength();
+  out.reserve(out.size() + len);
+
+  for (auto range : *head_) {
+    out.append(reinterpret_cast<const char*>(range.data()), range.size());
+  }
+}
+
+void IOBufQueue::gather(uint64_t maxLength) {
+  if (head_ != nullptr) {
+    head_->gather(maxLength);
+  }
+}
+
 } // folly