diff options
Diffstat (limited to 'libtransport/src/hicn/transport/utils/membuf.h')
-rw-r--r-- | libtransport/src/hicn/transport/utils/membuf.h | 921 |
1 files changed, 0 insertions, 921 deletions
diff --git a/libtransport/src/hicn/transport/utils/membuf.h b/libtransport/src/hicn/transport/utils/membuf.h deleted file mode 100644 index 9fc37dd25..000000000 --- a/libtransport/src/hicn/transport/utils/membuf.h +++ /dev/null @@ -1,921 +0,0 @@ -/* - * Copyright 2013-present Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * The code in this file if adapated from the IOBuf of folly: - * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h - */ - -#pragma once - -#include <hicn/transport/portability/portability.h> -#include <hicn/transport/utils/branch_prediction.h> - -#include <atomic> -#include <cassert> -#include <cinttypes> -#include <cstddef> -#include <cstring> -#include <iterator> -#include <limits> -#include <memory> -#include <type_traits> -#include <vector> - -#include <stdlib.h> - -#ifndef _WIN32 -TRANSPORT_GNU_DISABLE_WARNING("-Wshadow") -#endif - -namespace utils { - -class MemBuf { - public: - enum CreateOp { CREATE }; - enum WrapBufferOp { WRAP_BUFFER }; - enum TakeOwnershipOp { TAKE_OWNERSHIP }; - enum CopyBufferOp { COPY_BUFFER }; - - typedef void (*FreeFunction)(void* buf, void* userData); - - static std::unique_ptr<MemBuf> create(std::size_t capacity); - MemBuf(CreateOp, std::size_t capacity); - - /** - * Create a new MemBuf, using a single memory allocation to allocate space - * for both the MemBuf object and the data storage space. - * - * This saves one memory allocation. However, it can be wasteful if you - * later need to grow the buffer using reserve(). If the buffer needs to be - * reallocated, the space originally allocated will not be freed() until the - * MemBuf object itself is also freed. (It can also be slightly wasteful in - * some cases where you clone this MemBuf and then free the original MemBuf.) - */ - static std::unique_ptr<MemBuf> createCombined(std::size_t capacity); - - /** - * Create a new IOBuf, using separate memory allocations for the IOBuf object - * for the IOBuf and the data storage space. - * - * This requires two memory allocations, but saves space in the long run - * if you know that you will need to reallocate the data buffer later. - */ - static std::unique_ptr<MemBuf> createSeparate(std::size_t capacity); - - /** - * Allocate a new MemBuf chain with the requested total capacity, allocating - * no more than maxBufCapacity to each buffer. - */ - static std::unique_ptr<MemBuf> createChain(size_t totalCapacity, - std::size_t maxBufCapacity); - - static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity, - FreeFunction freeFn = nullptr, - void* userData = nullptr, - bool freeOnError = true) { - return takeOwnership(buf, capacity, capacity, freeFn, userData, - freeOnError); - } - - MemBuf(TakeOwnershipOp op, void* buf, std::size_t capacity, - FreeFunction freeFn = nullptr, void* userData = nullptr, - bool freeOnError = true) - : MemBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {} - - static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity, - std::size_t length, - FreeFunction freeFn = nullptr, - void* userData = nullptr, - bool freeOnError = true); - - MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, std::size_t length, - FreeFunction freeFn = nullptr, void* userData = nullptr, - bool freeOnError = true); - - static std::unique_ptr<MemBuf> wrapBuffer(const void* buf, - std::size_t capacity); - - static MemBuf wrapBufferAsValue(const void* buf, - std::size_t capacity) noexcept; - - MemBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept; - - /** - * Convenience function to create a new MemBuf object that copies data from a - * user-supplied buffer, optionally allocating a given amount of - * headroom and tailroom. - */ - static std::unique_ptr<MemBuf> copyBuffer(const void* buf, std::size_t size, - std::size_t headroom = 0, - std::size_t minTailroom = 0); - - MemBuf(CopyBufferOp op, const void* buf, std::size_t size, - std::size_t headroom = 0, std::size_t minTailroom = 0); - - /** - * Convenience function to free a chain of MemBufs held by a unique_ptr. - */ - static void destroy(std::unique_ptr<MemBuf>&& data) { - auto destroyer = std::move(data); - } - - ~MemBuf(); - - bool empty() const; - - const uint8_t* data() const { return data_; } - - uint8_t* writableData() { return data_; } - - const uint8_t* tail() const { return data_ + length_; } - - uint8_t* writableTail() { return data_ + length_; } - - std::size_t length() const { return length_; } - - std::size_t headroom() const { return std::size_t(data_ - buffer()); } - - std::size_t tailroom() const { return std::size_t(bufferEnd() - tail()); } - - const uint8_t* buffer() const { return buf_; } - - uint8_t* writableBuffer() { return buf_; } - - const uint8_t* bufferEnd() const { return buf_ + capacity_; } - - std::size_t capacity() const { return capacity_; } - - MemBuf* next() { return next_; } - - const MemBuf* next() const { return next_; } - - MemBuf* prev() { return prev_; } - - const MemBuf* prev() const { return prev_; } - - /** - * Shift the data forwards in the buffer. - * - * This shifts the data pointer forwards in the buffer to increase the - * headroom. This is commonly used to increase the headroom in a newly - * allocated buffer. - * - * The caller is responsible for ensuring that there is sufficient - * tailroom in the buffer before calling advance(). - * - * If there is a non-zero data length, advance() will use memmove() to shift - * the data forwards in the buffer. In this case, the caller is responsible - * for making sure the buffer is unshared, so it will not affect other MemBufs - * that may be sharing the same underlying buffer. - */ - void advance(std::size_t amount) { - // In debug builds, assert if there is a problem. - assert(amount <= tailroom()); - - if (length_ > 0) { - memmove(data_ + amount, data_, length_); - } - data_ += amount; - } - - /** - * Shift the data backwards in the buffer. - * - * The caller is responsible for ensuring that there is sufficient headroom - * in the buffer before calling retreat(). - * - * If there is a non-zero data length, retreat() will use memmove() to shift - * the data backwards in the buffer. In this case, the caller is responsible - * for making sure the buffer is unshared, so it will not affect other MemBufs - * that may be sharing the same underlying buffer. - */ - void retreat(std::size_t amount) { - // In debug builds, assert if there is a problem. - assert(amount <= headroom()); - - if (length_ > 0) { - memmove(data_ - amount, data_, length_); - } - data_ -= amount; - } - - void prepend(std::size_t amount) { - data_ -= amount; - length_ += amount; - } - - void append(std::size_t amount) { length_ += amount; } - - void trimStart(std::size_t amount) { - data_ += amount; - length_ -= amount; - } - - void trimEnd(std::size_t amount) { length_ -= amount; } - - // Never call clear on cloned membuf sharing different - // portions of the same underlying buffer. - // Use the trim functions instead. - void clear() { - data_ = writableBuffer(); - length_ = 0; - } - - void reserve(std::size_t minHeadroom, std::size_t minTailroom) { - // Maybe we don't need to do anything. - if (headroom() >= minHeadroom && tailroom() >= minTailroom) { - return; - } - // If the buffer is empty but we have enough total room (head + tail), - // move the data_ pointer around. - if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) { - data_ = writableBuffer() + minHeadroom; - return; - } - // Bah, we have to do actual work. - reserveSlow(minHeadroom, minTailroom); - } - - bool isChained() const { - assert((next_ == this) == (prev_ == this)); - return next_ != this; - } - - size_t countChainElements() const; - - std::size_t computeChainDataLength() const; - - void prependChain(std::unique_ptr<MemBuf>&& iobuf); - - void appendChain(std::unique_ptr<MemBuf>&& iobuf) { - // Just use prependChain() on the next element in our chain - next_->prependChain(std::move(iobuf)); - } - - std::unique_ptr<MemBuf> unlink() { - next_->prev_ = prev_; - prev_->next_ = next_; - prev_ = this; - next_ = this; - return std::unique_ptr<MemBuf>(this); - } - - /** - * Remove this MemBuf from its current chain and return a unique_ptr to - * the MemBuf that formerly followed it in the chain. - */ - std::unique_ptr<MemBuf> pop() { - MemBuf* next = next_; - next_->prev_ = prev_; - prev_->next_ = next_; - prev_ = this; - next_ = this; - return std::unique_ptr<MemBuf>((next == this) ? nullptr : next); - } - - /** - * Remove a subchain from this chain. - * - * Remove the subchain starting at head and ending at tail from this chain. - * - * Returns a unique_ptr pointing to head. (In other words, ownership of the - * head of the subchain is transferred to the caller.) If the caller ignores - * the return value and lets the unique_ptr be destroyed, the subchain will - * be immediately destroyed. - * - * The subchain referenced by the specified head and tail must be part of the - * same chain as the current MemBuf, but must not contain the current MemBuf. - * However, the specified head and tail may be equal to each other (i.e., - * they may be a subchain of length 1). - */ - std::unique_ptr<MemBuf> separateChain(MemBuf* head, MemBuf* tail) { - assert(head != this); - assert(tail != this); - - head->prev_->next_ = tail->next_; - tail->next_->prev_ = head->prev_; - - head->prev_ = tail; - tail->next_ = head; - - return std::unique_ptr<MemBuf>(head); - } - - /** - * Return true if at least one of the MemBufs in this chain are shared, - * or false if all of the MemBufs point to unique buffers. - * - * Use isSharedOne() to only check this MemBuf rather than the entire chain. - */ - bool isShared() const { - const MemBuf* current = this; - while (true) { - if (current->isSharedOne()) { - return true; - } - current = current->next_; - if (current == this) { - return false; - } - } - } - - /** - * Return true if all MemBufs 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 MemBuf* current = this; - while (true) { - if (!current->isManagedOne()) { - return false; - } - current = current->next_; - if (current == this) { - return true; - } - } - } - - /** - * Return true if this MemBuf 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 MemBufs are also pointing to the buffer used by this - * MemBuf, and false otherwise. - * - * If this MemBuf points at a buffer owned by another (non-MemBuf) part of the - * code (i.e., if the MemBuf was created using wrapBuffer(), or was cloned - * from such an MemBuf), it is always considered shared. - * - * This only checks the current MemBuf, and not other MemBufs in the chain. - */ - bool isSharedOne() const { - // If this is a user-owned buffer, it is always considered shared - if ((TRANSPORT_EXPECT_FALSE(!sharedInfo()))) { - return true; - } - - if ((TRANSPORT_EXPECT_FALSE(sharedInfo()->externallyShared))) { - return true; - } - - if ((TRANSPORT_EXPECT_TRUE(!(flags() & flag_maybe_shared)))) { - return false; - } - - // flag_maybe_shared is set, so we need to check the reference count. - // (Checking the reference count requires an atomic operation, which is why - // we prefer to only check flag_maybe_shared if possible.) - bool shared = sharedInfo()->refcount.load(std::memory_order_acquire) > 1; - if (!shared) { - // we're the last one left - clearFlags(flag_maybe_shared); - } - return shared; - } - - /** - * Ensure that this MemBuf has a unique buffer that is not shared by other - * MemBufs. - * - * unshare() operates on an entire chain of MemBuf objects. If the chain is - * shared, it may also coalesce the chain when making it unique. If the - * chain is coalesced, subsequent MemBuf objects in the current chain will be - * automatically deleted. - * - * Note that buffers owned by other (non-MemBuf) users are automatically - * considered shared. - * - * Throws std::bad_alloc on error. On error the MemBuf chain will be - * unmodified. - * - * Currently unshare may also throw std::overflow_error if it tries to - * coalesce. (TODO: In the future it would be nice if unshare() were smart - * enough not to coalesce the entire buffer if the data is too large. - * However, in practice this seems unlikely to become an issue.) - */ - void unshare() { - if (isChained()) { - unshareChained(); - } else { - unshareOne(); - } - } - - /** - * Ensure that this MemBuf has a unique buffer that is not shared by other - * MemBufs. - * - * unshareOne() operates on a single MemBuf object. This MemBuf will have a - * unique buffer after unshareOne() returns, but other MemBufs in the chain - * may still be shared after unshareOne() returns. - * - * Throws std::bad_alloc on error. On error the MemBuf will be unmodified. - */ - void unshareOne() { - if (isSharedOne()) { - unshareOneSlow(); - } - } - - /** - * 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 MemBuf, before it has been shared with other threads. - */ - void markExternallyShared(); - - /** - * Mark the underlying buffer that this MemBuf 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 MemBuf, before it has been shared with other threads. - */ - void markExternallySharedOne() { - SharedInfo* info = sharedInfo(); - if (info) { - info->externallyShared = true; - } - } - - /** - * Ensure that the memory that MemBufs in this chain refer to will continue to - * be allocated for as long as the MemBufs 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 MemBuf refers to will continue to be - * allocated for as long as this MemBuf (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 MemBuf chain into a single buffer. - // * - // * This method moves all of the data in this MemBuf chain into a single - // * contiguous buffer, if it is not already in one buffer. After coalesce() - // * returns, this MemBuf will be a chain of length one. Other MemBufs in - // the - // * chain will be automatically deleted. - // * - // * After coalescing, the MemBuf will have at least as much headroom as the - // * first MemBuf in the chain, and at least as much tailroom as the last - // MemBuf - // * in the chain. - // * - // * Throws std::bad_alloc on error. On error the MemBuf chain will be - // * unmodified. - // * - // * Returns ByteRange that points to the data MemBuf stores. - // */ - // ByteRange coalesce() { - // const std::size_t newHeadroom = headroom(); - // const std::size_t newTailroom = prev()->tailroom(); - // return coalesceWithHeadroomTailroom(newHeadroom, newTailroom); - // } - - // /** - // * This is similar to the coalesce() method, except this allows to set a - // * headroom and tailroom after coalescing. - // * - // * Returns ByteRange that points to the data MemBuf stores. - // */ - // ByteRange coalesceWithHeadroomTailroom(std::size_t newHeadroom, - // std::size_t newTailroom) { - // if (isChained()) { - // coalesceAndReallocate(newHeadroom, computeChainDataLength(), this, - // newTailroom); - // } - // return ByteRange(data_, length_); - // } - - /** - * Ensure that this chain has at least maxLength bytes available as a - * contiguous memory range. - * - * This method coalesces whole buffers in the chain into this buffer as - * necessary until this buffer's length() is at least maxLength. - * - * After coalescing, the MemBuf will have at least as much headroom as the - * first MemBuf in the chain, and at least as much tailroom as the last MemBuf - * that was coalesced. - * - * Throws std::bad_alloc or std::overflow_error on error. On error the MemBuf - * chain will be unmodified. Throws std::overflow_error if maxLength is - * longer than the total chain length. - * - * Upon return, either enough of the chain was coalesced into a contiguous - * region, or the entire chain was coalesced. That is, - * length() >= maxLength || !isChained() is true. - */ - void gather(std::size_t maxLength) { - if (!isChained() || length_ >= maxLength) { - return; - } - coalesceSlow(maxLength); - } - - /** - * Return a new MemBuf chain sharing the same data as this chain. - * - * The new MemBuf chain will normally point to the same underlying data - * buffers as the original chain. (The one exception to this is if some of - * the MemBufs in this chain contain small internal data buffers which cannot - * be shared.) - */ - std::unique_ptr<MemBuf> clone() const; - - /** - * Similar to clone(). But returns MemBuf by value rather than heap-allocating - * it. - */ - MemBuf cloneAsValue() const; - - /** - * Return a new MemBuf with the same data as this MemBuf. - * - * The new MemBuf returned will not be part of a chain (even if this MemBuf is - * part of a larger chain). - */ - std::unique_ptr<MemBuf> cloneOne() const; - - /** - * Similar to cloneOne(). But returns MemBuf by value rather than - * heap-allocating it. - */ - MemBuf cloneOneAsValue() const; - - /** - * Return a new unchained MemBuf that may share the same data as this chain. - * - * If the MemBuf chain is not chained then the new MemBuf will point to the - * same underlying data buffer as the original chain. Otherwise, it will clone - * and coalesce the MemBuf chain. - * - * The new MemBuf will have at least as much headroom as the first MemBuf in - * the chain, and at least as much tailroom as the last MemBuf in the chain. - * - * Throws std::bad_alloc on error. - */ - std::unique_ptr<MemBuf> cloneCoalesced() const; - - /** - * This is similar to the cloneCoalesced() method, except this allows to set a - * headroom and tailroom for the new MemBuf. - */ - std::unique_ptr<MemBuf> cloneCoalescedWithHeadroomTailroom( - std::size_t newHeadroom, std::size_t newTailroom) const; - - /** - * Similar to cloneCoalesced(). But returns MemBuf by value rather than - * heap-allocating it. - */ - MemBuf cloneCoalescedAsValue() const; - - /** - * This is similar to the cloneCoalescedAsValue() method, except this allows - * to set a headroom and tailroom for the new MemBuf. - */ - MemBuf cloneCoalescedAsValueWithHeadroomTailroom( - std::size_t newHeadroom, std::size_t newTailroom) 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(MemBuf& other) const { other = cloneAsValue(); } - - /** - * Similar to CloneOne(). But to fill an existing MemBuf instead of a new - * MemBuf. - */ - void cloneOneInto(MemBuf& other) const { other = cloneOneAsValue(); } - - /** - * Return an iovector suitable for e.g. writev() - * - * auto iov = buf->getIov(); - * auto xfer = writev(fd, iov.data(), iov.size()); - * - * Naturally, the returned iovector is invalid if you modify the buffer - * chain. - */ - std::vector<struct iovec> getIov() const; - - /** - * Update an existing iovec array with the MemBuf 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(std::vector<struct iovec>* iov) const; - - /** - * Fill an iovec array with the MemBuf 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; - - /** - * A helper that wraps a number of iovecs into an MemBuf chain. If count == - * 0, then a zero length buf is returned. This function never returns - * nullptr. - */ - static std::unique_ptr<MemBuf> wrapIov(const iovec* vec, size_t count); - - /** - * A helper that takes ownerships a number of iovecs into an MemBuf chain. If - * count == 0, then a zero length buf is returned. This function never - * returns nullptr. - */ - static std::unique_ptr<MemBuf> takeOwnershipIov(const iovec* vec, - size_t count, - FreeFunction freeFn = nullptr, - void* userData = nullptr, - bool freeOnError = true); - - /* - * Overridden operator new and delete. - * These perform specialized memory management to help support - * createCombined(), which allocates MemBuf objects together with the buffer - * data. - */ - void* operator new(size_t size); - void* operator new(size_t size, void* ptr); - void operator delete(void* ptr); - void operator delete(void* ptr, void* placement); - - // /** - // * Iteration support: a chain of MemBufs may be iterated through using - // * STL-style iterators over const ByteRanges. Iterators are only - // invalidated - // * if the MemBuf that they currently point to is removed. - // */ - // Iterator cbegin() const; - // Iterator cend() const; - // Iterator begin() const; - // Iterator end() const; - - /** - * Allocate a new null buffer. - * - * This can be used to allocate an empty MemBuf on the stack. It will have no - * space allocated for it. This is generally useful only to later use move - * assignment to fill out the MemBuf. - */ - MemBuf() noexcept; - - /** - * Move constructor and assignment operator. - * - * In general, you should only ever move the head of an MemBuf chain. - * Internal nodes in an MemBuf chain are owned by the head of the chain, and - * should not be moved from. (Technically, nothing prevents you from moving - * a non-head node, but the moved-to node will replace the moved-from node in - * the chain. This has implications for ownership, since non-head nodes are - * owned by the chain head. You are then responsible for relinquishing - * ownership of the moved-to node, and manually deleting the moved-from - * node.) - * - * With the move assignment operator, the destination of the move should be - * the head of an MemBuf chain or a solitary MemBuf not part of a chain. If - * the move destination is part of a chain, all other MemBufs in the chain - * will be deleted. - */ - MemBuf(MemBuf&& other) noexcept; - MemBuf& operator=(MemBuf&& other) noexcept; - - MemBuf(const MemBuf& other); - MemBuf& operator=(const MemBuf& other); - - private: - enum FlagsEnum : uintptr_t { - // Adding any more flags would not work on 32-bit architectures, - // as these flags are stashed in the least significant 2 bits of a - // max-align-aligned pointer. - flag_free_shared_info = 0x1, - flag_maybe_shared = 0x2, - flag_mask = flag_free_shared_info | flag_maybe_shared - }; - - struct SharedInfo { - SharedInfo(); - SharedInfo(FreeFunction fn, void* arg); - - // A pointer to a function to call to free the buffer when the refcount - // hits 0. If this is null, free() will be used instead. - FreeFunction freeFn; - void* userData; - std::atomic<uint32_t> refcount; - bool externallyShared{false}; - }; - // Helper structs for use by operator new and delete - struct HeapPrefix; - struct HeapStorage; - struct HeapFullStorage; - - /** - * Create a new MemBuf pointing to an external buffer. - * - * The caller is responsible for holding a reference count for this new - * MemBuf. The MemBuf constructor does not automatically increment the - * reference count. - */ - struct InternalConstructor {}; // avoid conflicts - MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, - std::size_t capacity, uint8_t* data, std::size_t length) noexcept; - - 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 - // end (no truncation) - void coalesceAndReallocate(size_t newHeadroom, size_t newLength, MemBuf* end, - size_t newTailroom); - void coalesceAndReallocate(size_t newLength, MemBuf* end) { - coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom()); - } - void decrementRefcount(); - void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom); - void freeExtBuffer(); - - static size_t goodExtBufferSize(std::size_t minCapacity); - static void initExtBuffer(uint8_t* buf, size_t mallocSize, - SharedInfo** infoReturn, - std::size_t* capacityReturn); - static void allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn, - SharedInfo** infoReturn, - std::size_t* capacityReturn); - static void releaseStorage(HeapStorage* storage, uint16_t freeFlags); - static void freeInternalBuf(void* buf, void* userData); - - /* - * Member variables - */ - - /* - * Links to the next and the previous MemBuf in this chain. - * - * The chain is circularly linked (the last element in the chain points back - * at the head), and next_ and prev_ can never be null. If this MemBuf is the - * only element in the chain, next_ and prev_ will both point to this. - */ - MemBuf* next_{this}; - MemBuf* prev_{this}; - - /* - * A pointer to the start of the data referenced by this MemBuf, and the - * length of the data. - * - * This may refer to any subsection of the actual buffer capacity. - */ - uint8_t* data_{nullptr}; - uint8_t* buf_{nullptr}; - std::size_t length_{0}; - std::size_t capacity_{0}; - - // Pack flags in least significant 2 bits, sharedInfo in the rest - mutable uintptr_t flags_and_shared_info_{0}; - - static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags, - SharedInfo* info) { - uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); - return flags | uinfo; - } - - inline SharedInfo* sharedInfo() const { - return reinterpret_cast<SharedInfo*>(flags_and_shared_info_ & ~flag_mask); - } - - inline void setSharedInfo(SharedInfo* info) { - uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); - flags_and_shared_info_ = (flags_and_shared_info_ & flag_mask) | uinfo; - } - - inline uintptr_t flags() const { return flags_and_shared_info_ & flag_mask; } - - // flags_ are changed from const methods - inline void setFlags(uintptr_t flags) const { - flags_and_shared_info_ |= flags; - } - - inline void clearFlags(uintptr_t flags) const { - flags_and_shared_info_ &= ~flags; - } - - inline void setFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) { - flags_and_shared_info_ = packFlagsAndSharedInfo(flags, info); - } - - struct DeleterBase { - virtual ~DeleterBase() {} - virtual void dispose(void* p) = 0; - }; - - template <class UniquePtr> - struct UniquePtrDeleter : public DeleterBase { - typedef typename UniquePtr::pointer Pointer; - typedef typename UniquePtr::deleter_type Deleter; - - explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {} - void dispose(void* p) override { - try { - deleter_(static_cast<Pointer>(p)); - delete this; - } catch (...) { - abort(); - } - } - - private: - Deleter deleter_; - }; - - static void freeUniquePtrBuffer(void* ptr, void* userData) { - static_cast<DeleterBase*>(userData)->dispose(ptr); - } -}; - -// template <class UniquePtr> -// typename std::enable_if< -// detail::IsUniquePtrToSL<UniquePtr>::value, -// std::unique_ptr<MemBuf>>::type -// MemBuf::takeOwnership(UniquePtr&& buf, size_t count) { -// size_t size = count * sizeof(typename UniquePtr::element_type); -// auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter()); -// return takeOwnership( -// buf.release(), size, &MemBuf::freeUniquePtrBuffer, deleter); -// } - -inline std::unique_ptr<MemBuf> MemBuf::copyBuffer(const void* data, - std::size_t size, - std::size_t headroom, - std::size_t minTailroom) { - std::size_t capacity = headroom + size + minTailroom; - std::unique_ptr<MemBuf> buf = MemBuf::create(capacity); - buf->advance(headroom); - if (size != 0) { - memcpy(buf->writableData(), data, size); - } - buf->append(size); - return buf; -} - -} // namespace utils |