Ensure curly-braces around control-flow
[folly.git] / folly / io / IOBufQueue.h
index a9065a486842d9e964252743dcd89b94c54863fb..c801920d34f793cd9e60608af254207652657564 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.
  * limitations under the License.
  */
 
-#ifndef FOLLY_IO_IOBUF_QUEUE_H
-#define FOLLY_IO_IOBUF_QUEUE_H
+#pragma once
 
-#include "folly/io/IOBuf.h"
+#include <folly/io/IOBuf.h>
 
 #include <stdexcept>
 #include <string>
@@ -54,18 +53,18 @@ class IOBufQueue {
   /**
    * Return a space to prepend bytes and the amount of headroom available.
    */
-  std::pair<void*, uint32_t> headroom();
+  std::pair<void*, uint64_t> headroom();
 
   /**
    * Indicate that n bytes from the headroom have been used.
    */
-  void markPrepended(uint32_t n);
+  void markPrepended(uint64_t n);
 
   /**
    * Prepend an existing range; throws std::overflow_error if not enough
    * room.
    */
-  void prepend(const void* buf, uint32_t n);
+  void prepend(const void* buf, uint64_t n);
 
   /**
    * Add a buffer or buffer chain to the end of this queue. The
@@ -98,8 +97,8 @@ class IOBufQueue {
    * Copy a string to the end of this queue.
    * The caller retains ownership of the source data.
    */
-  void append(const std::string& buf) {
-    append(buf.data(), buf.length());
+  void append(StringPiece sp) {
+    append(sp.data(), sp.size());
   }
 
   /**
@@ -115,7 +114,7 @@ class IOBufQueue {
    * Importantly, this method may be used to wrap buffers larger than 4GB.
    */
   void wrapBuffer(const void* buf, size_t len,
-                  uint32_t blockSize=(1U << 31));  // default block size: 2GB
+                  uint64_t blockSize=(1U << 31));  // default block size: 2GB
 
   /**
    * Obtain a writable block of contiguous bytes at the end of this
@@ -137,9 +136,17 @@ class IOBufQueue {
    *       callback, tell the application how much of the buffer they've
    *       filled with data.
    */
-  std::pair<void*,uint32_t> preallocate(
-    uint32_t min, uint32_t newAllocationSize,
-    uint32_t max = std::numeric_limits<uint32_t>::max());
+  std::pair<void*,uint64_t> preallocate(
+    uint64_t min, uint64_t newAllocationSize,
+    uint64_t max = std::numeric_limits<uint64_t>::max()) {
+    auto buf = tailBuf();
+    if (LIKELY(buf && buf->tailroom() >= min)) {
+      return std::make_pair(buf->writableTail(),
+                            std::min(max, buf->tailroom()));
+    }
+
+    return preallocateSlow(min, newAllocationSize, max);
+  }
 
   /**
    * Tell the queue that the caller has written data into the first n
@@ -151,18 +158,31 @@ class IOBufQueue {
    *       invoke any other non-const methods on this IOBufQueue between
    *       the call to preallocate and the call to postallocate().
    */
-  void postallocate(uint32_t n);
+  void postallocate(uint64_t n) {
+    head_->prev()->append(n);
+    chainLength_ += n;
+  }
 
   /**
    * Obtain a writable block of n contiguous bytes, allocating more space
    * if necessary, and mark it as used.  The caller can fill it later.
    */
-  void* allocate(uint32_t n) {
+  void* allocate(uint64_t n) {
     void* p = preallocate(n, n).first;
     postallocate(n);
     return p;
   }
 
+  void* writableTail() const {
+    auto buf = tailBuf();
+    return buf ? buf->writableTail() : nullptr;
+  }
+
+  size_t tailroom() const {
+    auto buf = tailBuf();
+    return buf ? buf->tailroom() : 0;
+  }
+
   /**
    * Split off the first n bytes of the queue into a separate IOBuf chain,
    * and transfer ownership of the new chain to the caller.  The IOBufQueue
@@ -175,7 +195,17 @@ class IOBufQueue {
    * @throws std::underflow_error if n exceeds the number of bytes
    *         in the queue.
    */
-  std::unique_ptr<folly::IOBuf> split(size_t n);
+  std::unique_ptr<folly::IOBuf> split(size_t n) {
+    return split(n, true);
+  }
+
+  /**
+   * Similar to split, but will return the entire queue instead of throwing
+   * if n exceeds the number of bytes in the queue.
+   */
+  std::unique_ptr<folly::IOBuf> splitAtMost(size_t n) {
+    return split(n, false);
+  }
 
   /**
    * Similar to IOBuf::trimStart, but works on the whole queue.  Will
@@ -183,12 +213,24 @@ class IOBufQueue {
    */
   void trimStart(size_t amount);
 
+  /**
+   * Similar to trimStart, but will trim at most amount bytes and returns
+   * the number of bytes trimmed.
+   */
+  size_t trimStartAtMost(size_t amount);
+
   /**
    * Similar to IOBuf::trimEnd, but works on the whole queue.  Will
    * pop off buffers that have been completely trimmed.
    */
   void trimEnd(size_t amount);
 
+  /**
+   * Similar to trimEnd, but will trim at most amount bytes and returns
+   * the number of bytes trimmed.
+   */
+  size_t trimEndAtMost(size_t amount);
+
   /**
    * Transfer ownership of the queue's entire IOBuf chain to the caller.
    */
@@ -204,37 +246,82 @@ class IOBufQueue {
     return head_.get();
   }
 
+  /**
+   * returns the first IOBuf in the chain and removes it from the chain
+   *
+   * @return first IOBuf in the chain or nullptr if none.
+   */
+  std::unique_ptr<folly::IOBuf> pop_front();
+
   /**
    * Total chain length, only valid if cacheLength was specified in the
    * constructor.
    */
   size_t chainLength() const {
-    if (!options_.cacheChainLength) {
+    if (UNLIKELY(!options_.cacheChainLength)) {
       throw std::invalid_argument("IOBufQueue: chain length not cached");
     }
     return chainLength_;
   }
 
+  /**
+   * Returns true iff the IOBuf chain length is 0.
+   */
+  bool empty() const {
+    return !head_ || head_->empty();
+  }
+
   const Options& options() const {
     return options_;
   }
 
+  /**
+   * Clear the queue.  Note that this does not release the buffers, it
+   * just sets their length to zero; useful if you want to reuse the
+   * same queue without reallocating.
+   */
+  void clear();
+
+  /**
+   * Append the queue to a std::string. Non-destructive.
+   */
+  void appendToString(std::string& out) const;
+
+  /**
+   * Calls IOBuf::gather() on the head of the queue, if it exists.
+   */
+  void gather(uint64_t maxLength);
+
   /** Movable */
-  IOBufQueue(IOBufQueue&&);
+  IOBufQueue(IOBufQueue&&) noexcept;
   IOBufQueue& operator=(IOBufQueue&&);
 
  private:
+  IOBuf* tailBuf() const {
+    if (UNLIKELY(!head_)) {
+      return nullptr;
+    }
+    IOBuf* buf = head_->prev();
+    return LIKELY(!buf->isSharedOne()) ? buf : nullptr;
+  }
+  std::pair<void*,uint64_t> preallocateSlow(
+    uint64_t min, uint64_t newAllocationSize, uint64_t max);
+
+  std::unique_ptr<folly::IOBuf> split(size_t n, bool throwOnUnderflow);
+
   static const size_t kChainLengthNotCached = (size_t)-1;
   /** Not copyable */
   IOBufQueue(const IOBufQueue&) = delete;
   IOBufQueue& operator=(const IOBufQueue&) = delete;
 
   Options options_;
+
+  // NOTE that chainLength_ is still updated even if !options_.cacheChainLength
+  // because doing it unchecked in postallocate() is faster (no (mis)predicted
+  // branch)
   size_t chainLength_;
   /** Everything that has been appended but not yet discarded or moved out */
   std::unique_ptr<folly::IOBuf> head_;
 };
 
-} // folly
-
-#endif // FOLLY_IO_IOBUF_QUEUE_H
+} // namespace folly