X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Fio%2FIOBuf.h;h=15f8839c840edb0556cf411e368aa3d69a455a4d;hb=b3e7df8220f410398011fea71b280ba8be459fcc;hp=402293436ad0526a9117f97abe683983e3b0b808;hpb=81823a9cb036baed2a3cfe5b352832e6340e6a39;p=folly.git diff --git a/folly/io/IOBuf.h b/folly/io/IOBuf.h index 40229343..15f8839c 100644 --- a/folly/io/IOBuf.h +++ b/folly/io/IOBuf.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 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,8 +14,7 @@ * limitations under the License. */ -#ifndef FOLLY_IO_IOBUF_H_ -#define FOLLY_IO_IOBUF_H_ +#pragma once #include #include @@ -25,14 +24,14 @@ #include #include #include -#include #include #include -#include "folly/FBString.h" -#include "folly/Range.h" -#include "folly/FBVector.h" +#include +#include +#include +#include // Ignore shadowing warnings within this file, so includers can use -Wshadow. #pragma GCC diagnostic push @@ -189,13 +188,9 @@ namespace folly { * an IOBuf chain must be heap allocated. (All functions to add nodes to a * chain require a std::unique_ptr, which enforces this requrement.) * - * Additionally, no copy-constructor or assignment operator currently exists, - * so stack-allocated IOBufs may only be moved, not copied. (Technically - * nothing is preventing us from adding a copy constructor and assignment - * operator. However, it seems like this would add the possibility for some - * confusion. We would need to determine if these functions would copy just a - * single buffer, or the entire chain.) - * + * Copying IOBufs is only meaningful for the head of a chain. The entire chain + * is cloned; the IOBufs will become shared, and the old and new IOBufs will + * refer to the same underlying memory. * * IOBuf Sharing * ------------- @@ -376,6 +371,16 @@ class IOBuf { static std::unique_ptr wrapBuffer(ByteRange br) { return wrapBuffer(br.data(), br.size()); } + + /** + * Similar to wrapBuffer(), but returns IOBuf by value rather than + * heap-allocating it. + */ + static IOBuf wrapBufferAsValue(const void* buf, uint64_t capacity); + static IOBuf wrapBufferAsValue(ByteRange br) { + return wrapBufferAsValue(br.data(), br.size()); + } + IOBuf(WrapBufferOp op, const void* buf, uint64_t capacity); IOBuf(WrapBufferOp op, ByteRange br); @@ -502,7 +507,7 @@ class IOBuf { * Returns the number of bytes in the buffer before the start of the data. */ uint64_t headroom() const { - return data_ - buffer(); + return uint64_t(data_ - buffer()); } /** @@ -511,7 +516,7 @@ class IOBuf { * Returns the number of bytes in the buffer after the end of the data. */ uint64_t tailroom() const { - return bufferEnd() - tail(); + return uint64_t(bufferEnd() - tail()); } /** @@ -875,6 +880,33 @@ class IOBuf { } } + /** + * Return true if all IOBufs in this chain are managed by the usual + * refcounting mechanism (and so the lifetime of the underlying memory + * can be extended by clone()). + */ + bool isManaged() const { + const IOBuf* current = this; + while (true) { + if (!current->isManagedOne()) { + return false; + } + current = current->next_; + if (current == this) { + return true; + } + } + } + + /** + * Return true if this IOBuf is managed by the usual refcounting mechanism + * (and so the lifetime of the underlying memory can be extended by + * cloneOne()). + */ + bool isManagedOne() const { + return sharedInfo(); + } + /** * Return true if other IOBufs are also pointing to the buffer used by this * IOBuf, and false otherwise. @@ -891,6 +923,10 @@ class IOBuf { return true; } + if (UNLIKELY(sharedInfo()->externallyShared)) { + return true; + } + if (LIKELY(!(flags() & kFlagMaybeShared))) { return false; } @@ -950,6 +986,63 @@ class IOBuf { } } + /** + * Mark the underlying buffers in this chain as shared with external memory + * management mechanism. This will make isShared() always returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an IOBuf, before it has been shared with other threads. + */ + void markExternallyShared(); + + /** + * Mark the underlying buffer that this IOBuf refers to as shared with + * external memory management mechanism. This will make isSharedOne() always + * returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an IOBuf, before it has been shared with other threads. + */ + void markExternallySharedOne() { + SharedInfo* info = sharedInfo(); + if (info) { + info->externallyShared = true; + } + } + + /** + * Ensure that the memory that IOBufs in this chain refer to will continue to + * be allocated for as long as the IOBufs of the chain (or any clone()s + * created from this point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManaged() { + if (isChained()) { + makeManagedChained(); + } else { + makeManagedOne(); + } + } + + /** + * Ensure that the memory that this IOBuf refers to will continue to be + * allocated for as long as this IOBuf (or any clone()s created from this + * point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManagedOne() { + if (!isManagedOne()) { + // We can call the internal function directly; unmanaged implies shared. + unshareOneSlow(); + } + } + /** * Coalesce this IOBuf chain into a single buffer. * @@ -1010,6 +1103,12 @@ class IOBuf { */ std::unique_ptr clone() const; + /** + * Similar to clone(). But returns IOBuf by value rather than heap-allocating + * it. + */ + IOBuf cloneAsValue() const; + /** * Return a new IOBuf with the same data as this IOBuf. * @@ -1018,17 +1117,47 @@ class IOBuf { */ std::unique_ptr cloneOne() const; + /** + * Similar to cloneOne(). But returns IOBuf by value rather than + * heap-allocating it. + */ + IOBuf cloneOneAsValue() const; + + /** + * Return a new unchained IOBuf that may share the same data as this chain. + * + * If the IOBuf chain is not chained then the new IOBuf will point to the same + * underlying data buffer as the original chain. Otherwise, it will clone and + * coalesce the IOBuf chain. + * + * The new IOBuf will have at least as much headroom as the first IOBuf in the + * chain, and at least as much tailroom as the last IOBuf in the chain. + * + * Throws std::bad_alloc on error. + */ + std::unique_ptr cloneCoalesced() const; + + /** + * Similar to cloneCoalesced(). But returns IOBuf by value rather than + * heap-allocating it. + */ + IOBuf cloneCoalescedAsValue() const; + /** * Similar to Clone(). But use other as the head node. Other nodes in the * chain (if any) will be allocted on heap. */ - void cloneInto(IOBuf& other) const; + void cloneInto(IOBuf& other) const { + other = cloneAsValue(); + } /** * Similar to CloneOne(). But to fill an existing IOBuf instead of a new * IOBuf. */ - void cloneOneInto(IOBuf& other) const; + void cloneOneInto(IOBuf& other) const { + other = cloneOneAsValue(); + } /** * Return an iovector suitable for e.g. writev() @@ -1041,6 +1170,29 @@ class IOBuf { */ folly::fbvector getIov() const; + /** + * Update an existing iovec array with the IOBuf data. + * + * New iovecs will be appended to the existing vector; anything already + * present in the vector will be left unchanged. + * + * Naturally, the returned iovec data will be invalid if you modify the + * buffer chain. + */ + void appendToIov(folly::fbvector* iov) const; + + /** + * Fill an iovec array with the IOBuf data. + * + * Returns the number of iovec filled. If there are more buffer than + * iovec, returns 0. This version is suitable to use with stack iovec + * arrays. + * + * Naturally, the filled iovec data will be invalid if you modify the + * buffer chain. + */ + size_t fillIov(struct iovec* iov, size_t len) const; + /* * Overridden operator new and delete. * These perform specialized memory management to help support @@ -1093,14 +1245,13 @@ class IOBuf { * the head of an IOBuf chain or a solitary IOBuf not part of a chain. If * the move destination is part of a chain, all other IOBufs in the chain * will be deleted. - * - * (We currently don't provide a copy constructor or assignment operator. - * The main reason is because it is not clear these operations should copy - * the entire chain or just the single IOBuf.) */ IOBuf(IOBuf&& other) noexcept; IOBuf& operator=(IOBuf&& other) noexcept; + IOBuf(const IOBuf& other); + IOBuf& operator=(const IOBuf& other); + private: enum FlagsEnum : uintptr_t { // Adding any more flags would not work on 32-bit architectures, @@ -1120,16 +1271,13 @@ class IOBuf { FreeFunction freeFn; void* userData; std::atomic refcount; + bool externallyShared{false}; }; // Helper structs for use by operator new and delete struct HeapPrefix; struct HeapStorage; struct HeapFullStorage; - // Forbidden copy constructor and assignment opererator - IOBuf(IOBuf const &); - IOBuf& operator=(IOBuf const &); - /** * Create a new IOBuf pointing to an external buffer. * @@ -1144,6 +1292,7 @@ class IOBuf { void unshareOneSlow(); void unshareChained(); + void makeManagedChained(); void coalesceSlow(); void coalesceSlow(size_t maxLength); // newLength must be the entire length of the buffers between this and @@ -1202,8 +1351,8 @@ class IOBuf { static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) { uintptr_t uinfo = reinterpret_cast(info); - DCHECK_EQ(flags & ~kFlagMask, 0); - DCHECK_EQ(uinfo & kFlagMask, 0); + DCHECK_EQ(flags & ~kFlagMask, 0u); + DCHECK_EQ(uinfo & kFlagMask, 0u); return flags | uinfo; } @@ -1213,7 +1362,7 @@ class IOBuf { inline void setSharedInfo(SharedInfo* info) { uintptr_t uinfo = reinterpret_cast(info); - DCHECK_EQ(uinfo & kFlagMask, 0); + DCHECK_EQ(uinfo & kFlagMask, 0u); flagsAndSharedInfo_ = (flagsAndSharedInfo_ & kFlagMask) | uinfo; } @@ -1223,12 +1372,12 @@ class IOBuf { // flags_ are changed from const methods inline void setFlags(uintptr_t flags) const { - DCHECK_EQ(flags & ~kFlagMask, 0); + DCHECK_EQ(flags & ~kFlagMask, 0u); flagsAndSharedInfo_ |= flags; } inline void clearFlags(uintptr_t flags) const { - DCHECK_EQ(flags & ~kFlagMask, 0); + DCHECK_EQ(flags & ~kFlagMask, 0u); flagsAndSharedInfo_ &= ~flags; } @@ -1265,6 +1414,33 @@ class IOBuf { } }; +/** + * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2. + */ +struct IOBufHash { + size_t operator()(const IOBuf& buf) const; + size_t operator()(const std::unique_ptr& buf) const { + return buf ? (*this)(*buf) : 0; + } +}; + +/** + * Equality predicate for IOBuf objects. Compares data in the entire chain. + */ +struct IOBufEqual { + bool operator()(const IOBuf& a, const IOBuf& b) const; + bool operator()(const std::unique_ptr& a, + const std::unique_ptr& b) const { + if (!a && !b) { + return true; + } else if (!a || !b) { + return false; + } else { + return (*this)(*a, *b); + } + } +}; + template typename std::enable_if::value, std::unique_ptr>::type @@ -1283,7 +1459,9 @@ inline std::unique_ptr IOBuf::copyBuffer( uint64_t capacity = headroom + size + minTailroom; std::unique_ptr buf = create(capacity); buf->advance(headroom); - memcpy(buf->writableData(), data, size); + if (size != 0) { + memcpy(buf->writableData(), data, size); + } buf->append(size); return buf; } @@ -1324,6 +1502,8 @@ class IOBuf::Iterator : public boost::iterator_facade< } } + Iterator() {} + private: void setVal() { val_ = ByteRange(pos_->data(), pos_->tail()); @@ -1354,8 +1534,8 @@ class IOBuf::Iterator : public boost::iterator_facade< adjustForEnd(); } - const IOBuf* pos_; - const IOBuf* end_; + const IOBuf* pos_{nullptr}; + const IOBuf* end_{nullptr}; ByteRange val_; }; @@ -1365,5 +1545,3 @@ inline IOBuf::Iterator IOBuf::end() const { return cend(); } } // folly #pragma GCC diagnostic pop - -#endif // FOLLY_IO_IOBUF_H_