/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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. */ // // Docs: https://fburl.com/fbcref_iobuf // #pragma once #include <atomic> #include <cassert> #include <cinttypes> #include <cstddef> #include <cstring> #include <iterator> #include <limits> #include <memory> #include <string> #include <type_traits> #include <glog/logging.h> #include <folly/FBString.h> #include <folly/FBVector.h> #include <folly/Function.h> #include <folly/Portability.h> #include <folly/Range.h> #include <folly/detail/Iterators.h> #include <folly/lang/CheckedMath.h> #include <folly/lang/Ordering.h> #include <folly/portability/SysUio.h> #include <folly/synchronization/MicroSpinLock.h> FOLLY_PUSH_WARNING // Ignore shadowing warnings within this file, so includers can use -Wshadow. FOLLY_GNU_DISABLE_WARNING(…) // Some compilers break on -Wdocumentation. Not all compilers recognise that // option, so we also suppress -Wpragmas FOLLY_GNU_DISABLE_WARNING(…) // Ignore documentation warnings, to enable overloads to share documentation // with differing parameters FOLLY_GNU_DISABLE_WARNING(…) namespace folly { namespace detail { // Is T a unique_ptr<> to a standard-layout type? template <typename T> struct IsUniquePtrToSL : std::false_type { … }; IsUniquePtrToSL<std::unique_ptr<T, D>>; } // namespace detail /** * IOBuf manages heap-allocated byte buffers. * * API Details * ----------- * * - The buffer is not necessarily full of meaningful bytes - there may be * uninitialized bytes before and after the central "valid" range of data. * - Buffers are refcounted, and can be shared by multiple IOBuf objects. * - If you ever write to an IOBuf, first use unshare() to get a unique copy. * - IOBufs can be "chained" in a circularly linked list. * - Use coalesce() to turn an IOBuf chain into a single IOBuf. * - IOBufs are not synchronized. The user is responsible for synchronization. * Notes: * - Like a shared_ptr, the refcounting is atomic. * - const IOBuf methods do not mutate any state, so can safely be called * concurrently with each other, as expected. * - IOBufs are typically stored on the heap, so that they can be used in * chains. * * * Data Layout * ----------- * * IOBuf objects contains a pointer to the buffer and information about which * segment of the buffer contains valid data. * * +-------+ * | IOBuf | * +-------+ * / * | |----- length() -----| * v * +------------+--------------------+-----------+ * | headroom | data | tailroom | * +------------+--------------------+-----------+ * ^ ^ ^ ^ * buffer() data() tail() bufferEnd() * * |----------------- capacity() ----------------| * * * Buffer Sharing * -------------- * * Each buffer is reference counted, and multiple IOBuf objects may point * to the same buffer. Each IOBuf may point to a different section of valid * data within the underlying buffer. For example, if multiple protocol * requests are read from the network into a single buffer, a separate IOBuf * may be created for each request, all sharing the same underlying buffer. * * In other words, when multiple IOBufs share the same underlying buffer, the * data() and tail() methods on each IOBuf may point to a different segment of * the data. However, the buffer() and bufferEnd() methods will point to the * same location for all IOBufs sharing the same underlying buffer, unless the * tail was resized by trimWritableTail() or maybeSplitTail(). * * +-----------+ +---------+ * | IOBuf 1 | | IOBuf 2 | * +-----------+ +---------+ * | | _____/ | * data | tail |/ data | tail * v v v * +-------------------------------------+ * | | | | | * +-------------------------------------+ * * If you only read data from an IOBuf, you don't need to worry about other * IOBuf objects possibly sharing the same underlying buffer. However, if you * ever write to the buffer you need to first ensure that no other IOBufs point * to the same buffer. The unshare() method may be used to ensure that you * have an unshared buffer. * * * IOBuf Chains * ------------ * * IOBuf objects also contain pointers to next and previous IOBuf objects. * This can be used to represent a single logical piece of data that is stored * in non-contiguous chunks in separate buffers. * * +---------------------------------------------------------------+ * | | * | +-----------+ +-----------+ +-----------+ | * +--> | IOBuf 1 | -----> | IOBuf 2 | -----> | IOBuf 3 | ---+ * +-----------+ +-----------+ +-----------+ * | | _________/ | ___/ \__ * | |/ | / \ * v v v v v * +-------------------------------------+ +-----------------+ * | | | | | | | * +-------------------------------------+ +-----------------+ * * A single IOBuf object can only belong to one chain at a time. * * IOBuf chains are always circular. The "prev" pointer in the head of the * chain points to the tail of the chain. However, it is up to the user to * decide which IOBuf is the head. Internally the IOBuf code does not care * which element is the head. * * The lifetime of all IOBufs in the chain are linked: when one element in the * chain is deleted, all other chained elements are also deleted. Conceptually * it is simplest to treat this as if the head of the chain owns all other * IOBufs in the chain. When you delete the head of the chain, it will delete * the other elements as well. For this reason, appendToChain() and * insertAfterThisOne() take ownership of the new elements being added to this * chain. * * When the coalesce() method is used to coalesce an entire IOBuf chain into a * single IOBuf, all other IOBufs in the chain are eliminated and automatically * deleted. The unshare() method may coalesce the chain; if it does it will * similarly delete all IOBufs eliminated from the chain. * * As discussed in the following section, it is up to the user to maintain a * lock around the entire IOBuf chain if multiple threads need to access the * chain. IOBuf does not provide any internal locking. * * * Synchronization * --------------- * * When used in multithread programs, a single IOBuf object should only be * accessed mutably by a single thread at a time. All const member functions of * IOBuf are safe to call concurrently with one another, but when a caller uses * a single IOBuf across multiple threads and at least one thread calls a * non-const member function, the caller is responsible for using an external * lock to synchronize access to the IOBuf. * * Two separate IOBuf objects may be accessed concurrently in separate threads * without locking, even if they point to the same underlying buffer. The * buffer reference count is always accessed atomically, and no other * operations should affect other IOBufs that point to the same data segment. * The caller is responsible for using unshare() to ensure that the data buffer * is not shared by other IOBufs before writing to it, and this ensures that * the data itself is not modified in one thread while also being accessed from * another thread. * * For IOBuf chains, no two IOBufs in the same chain should be accessed * simultaneously in separate threads, except where all simultaneous accesses * are to const member functions. The caller must maintain a lock around the * entire chain if the chain, or individual IOBufs in the chain, may be accessed * by multiple threads with at least one of the threads needing to mutate. * * * IOBuf Object Allocation * ----------------------- * * IOBuf objects themselves exist separately from the data buffer they point * to. Therefore one must also consider how to allocate and manage the IOBuf * objects. Typically, IOBufs are allocated on the heap. * * +--------------+ * | unique_ptr | * +--------------+ * | * v * +---------+ * | IOBuf | * +---------+ * | * v * +----------+ * | buffer | * +----------+ * * * It is more common to allocate IOBuf objects on the heap, using the create(), * takeOwnership(), or wrapBuffer() factory functions. The clone()/cloneOne() * functions also return new heap-allocated IOBufs. The createCombined() * function allocates the IOBuf object and data storage space together, in a * single memory allocation. This can improve performance, particularly if you * know that the data buffer and the IOBuf itself will have similar lifetimes. * * That said, it is also possible to allocate IOBufs on the stack or inline * inside another object as well. This is useful for cases where the IOBuf is * short-lived, or when the overhead of allocating the IOBuf on the heap is * undesirable. * * However, note that stack-allocated IOBufs may only be used as the head of a * chain (or standalone as the only IOBuf in a chain). All non-head members of * an IOBuf chain must be heap allocated. (All functions to add nodes to a * chain require a std::unique_ptr<IOBuf>, which enforces this requirement.) * * 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 * ------------- * * The IOBuf class manages sharing of the underlying buffer that it points to, * maintaining a reference count if multiple IOBufs are pointing at the same * buffer. * * However, it is the callers responsibility to manage sharing and ownership of * IOBuf objects themselves. The IOBuf structure does not provide room for an * intrusive refcount on the IOBuf object itself, only the underlying data * buffer is reference counted. If users want to share the same IOBuf object * between multiple parts of the code, they are responsible for managing this * sharing on their own. (For example, by using a shared_ptr. Alternatively, * users always have the option of using clone() to create a second IOBuf that * points to the same underlying buffer.) * * * Inspiration * ----------- * * IOBuf objects are intended to be used primarily for networking code, and are * modelled somewhat after FreeBSD's mbuf data structure, and Linux's sk_buff * structure. * * IOBuf objects facilitate zero-copy network programming, by allowing multiple * IOBuf objects to point to the same underlying buffer of data, using a * reference count to track when the buffer is no longer needed and can be * freed. * * * @refcode folly/docs/examples/folly/io/IOBuf.cpp */ class IOBuf { … }; /** * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2. */ struct IOBufHash { … }; /** * Ordering for IOBuf objects. Compares data in the entire chain. */ struct IOBufCompare { … }; /** * Equality predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufEqualTo : compare_equal_to<IOBufCompare> { … }; /** * Inequality predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufNotEqualTo : compare_not_equal_to<IOBufCompare> { … }; /** * Less predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufLess : compare_less<IOBufCompare> { … }; /** * At-most predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufLessEqual : compare_less_equal<IOBufCompare> { … }; /** * Greater predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufGreater : compare_greater<IOBufCompare> { … }; /** * At-least predicate for IOBuf objects. Compares data in the entire chain. */ struct IOBufGreaterEqual : compare_greater_equal<IOBufCompare> { … }; template <class UniquePtr> typename std::enable_if< detail::IsUniquePtrToSL<UniquePtr>::value, std::unique_ptr<IOBuf>>::type IOBuf::takeOwnership(UniquePtr&& buf, size_t count) { … } inline std::unique_ptr<IOBuf> IOBuf::copyBuffer( const void* data, std::size_t size, std::size_t headroom, std::size_t minTailroom) { … } inline std::unique_ptr<IOBuf> IOBuf::copyBuffer( StringPiece buf, std::size_t headroom, std::size_t minTailroom) { … } inline std::unique_ptr<IOBuf> IOBuf::maybeCopyBuffer( StringPiece buf, std::size_t headroom, std::size_t minTailroom) { … } class IOBuf::Iterator : public detail::IteratorFacade< IOBuf::Iterator, ByteRange const, std::forward_iterator_tag> { … }; inline IOBuf::Iterator IOBuf::begin() const { … } inline IOBuf::Iterator IOBuf::end() const { … } template <class Container> void IOBuf::appendTo(Container& container) const { … } template <class Container> Container IOBuf::to() const { … } } // namespace folly FOLLY_POP_WARNING