IOBuf manages heap-allocated byte buffers.
Declared in <folly/io/IOBuf.h>
class IOBuf;
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
| Name | Description |
|---|---|
FillIovResult | Result of filling an iovec array with IOBuf data. |
Iterator | Forward iterator over the buffers in an IOBuf chain. |
| Name | Description |
|---|---|
FreeFunction | Custom free function used to release an externally-owned buffer. |
const_iterator | Const iterator over the buffers in a chain. |
iterator | Iterator over the buffers in a chain. |
value_type | The element type produced when iterating a chain. |
| Name | Description |
|---|---|
CombinedOption | Controls whether the IOBuf and its buffer share one allocation. |
CopyBufferOp | Tag type selecting the copy-buffer constructor. |
CreateOp | Tag type selecting the create constructor. |
SizedFree | Tag type indicating a sized-free deleter. |
TakeOwnershipOp | Tag type selecting the take-ownership constructor. |
WrapBufferOp | Tag type selecting the wrap-buffer constructor. |
| Name | Description |
|---|---|
IOBuf [constructor] | Constructors |
~IOBuf [destructor] | Destroy this IOBuf. |
operator= | Assignment operators |
advance | Shift the data forwards in the buffer. |
append | Adjust the tail pointer to include more valid data at the end. |
appendChain | Deprecated name for insertAfterThisOne() |
appendSharedInfoObserver | Add an Observer to the refcount block (SharedInfo). |
appendTo | Append the chain data into the provided container. |
appendToChain | Append another IOBuf chain to the end of this chain. |
appendToIov | Update an existing iovec array with the IOBuf data. |
approximateShareCountOne | Inconsistently get the reference count. |
begin | Get an iterator to the first IOBuf in this chain. |
buffer | Get the pointer to the start of the buffer. |
bufferEnd | Get the pointer to the end of the buffer. |
capacity | Get the total size of the buffer. |
cbegin | Iterate over the IOBufs in this chain. |
cend | Get an iterator past the last IOBuf in this chain. |
clear | Clear the buffer. |
clone | clone overloads |
cloneAsValue | Copy an IOBuf chain. |
cloneCoalesced | Copy an IOBuf chain into a single buffer. |
cloneCoalescedAsValue | Copy an IOBuf chain into a single buffer. |
cloneCoalescedAsValueWithHeadroomTailroom | Copy an IOBuf chain into a single buffer. |
cloneCoalescedWithHeadroomTailroom | Copy an IOBuf chain into a single buffer. |
cloneInto | Copy an IOBuf chain. |
cloneOne | cloneOne overloads |
cloneOneAsValue | Copy an individual IOBuf. |
cloneOneInto | Copy an individual IOBuf. |
coalesce | Coalesce this IOBuf chain into a single buffer. |
coalesceWithHeadroomTailroom | Coalesce this IOBuf chain into a single buffer. |
computeChainCapacity | Get the capacity all IOBufs in the chain. |
computeChainDataLength | Get the length of all the data in this IOBuf chain. |
countChainElements | Get the number of IOBufs in this chain. |
data | Get the pointer to the start of the data. |
empty | Check whether the chain is empty. |
end | Get an iterator past the last IOBuf in this chain. |
fillIov | Fill an iovec array with the IOBuf data. |
gather | Ensure that this chain has at least contiguousLength bytes available as a contiguous memory range. |
getFreeFn | Get the FreeFunction. |
getIov | Get an iovector suitable for e.g. writev() |
getUserData | Get userData. |
headroom | Get the amount of head room. |
insertAfterThisOne | Insert an IOBuf chain immediately after this chain element. |
isChained | Is this IOBuf part of a chain. |
isManaged | Check if all IOBufs in this chain use the standard refcounting mechanism. |
isManagedOne | Check if this IOBuf uses the standard refcounting mechanism. |
isShared | Check if any chain buffers are shared. |
isSharedOne | Check if the buffer is shared. |
length | Get the size of the data for this individual IOBuf in the chain. |
makeManaged | Ensure that the buffers are owned by the IOBuf chain. |
makeManagedOne | Ensure that the buffer is owned by the IOBuf. |
markExternallyShared | Mark the underlying buffers in this chain as shared. |
markExternallySharedOne | Mark the underlying buffer as shared. |
maybeSplitTail | Returns a new IOBuf whose buffer is this buffer's tail. The latter is trimmed to 0 to relinquish ownership of it. The returned IOBuf is unshared, and it holds a shared reference to the IOBuf that originally owned the buffer, extending its lifetime. |
moveToFbString | Destructively convert to an fbstring. |
next | Get a pointer to the next IOBuf in this chain. |
operator delete | Delete operators |
operator new | New operators |
pop | Remove the rest of the chain from this IOBuf. |
prepend | Adjust the data pointer to include more valid data at the beginning. |
prependChain | Deprecated name for appendToChain() |
prev | Get a pointer to the previous IOBuf in this chain. |
reserve | Ensure that the buffer has enough free space. |
retreat | Shift the data backwards in the buffer. |
separateChain | Remove a subchain from this chain. |
tail | Get the pointer to the end of the data. |
tailroom | Get the amount of tail room. |
to | Returns a container containing the chain data. |
toString | Convenience version of to<std::string>() that works when called on a dependent name in a template function without having to use the "template" keyword. |
trimEnd | Adjust the tail pointer backwards to include less valid data. |
trimStart | Adjust the data pointer to include less valid data. |
trimWritableTail | Adjust the buffer end pointer to reduce the buffer capacity. |
unlink | Remove this IOBuf from its current chain. |
unshare | Ensure that this IOBuf chain has unique, unshared buffers. |
unshareOne | Ensure that this IOBuf has a unique, unshared buffer. |
writableBuffer | Get a writable pointer to the start of the buffer. |
writableData | Get a writable pointer to the start of the data. |
writableTail | Get a writable pointer to the end of the data. |
| Name | Description |
|---|---|
copyBuffer | Create an IOBuf and copy data into the buffer. |
create | Create an IOBuf with the requested capacity. |
createChain | Create a new IOBuf chain. |
createCombined | Create an IOBuf, allocated alongside its buffer. |
createSeparate | Create an IOBuf, allocated separately from its buffer. |
destroy | Free an IOBuf. |
fromString | fromString overloads |
goodSize | Get a good malloc size. |
maybeCopyBuffer | Create an IOBuf and copy string data into the buffer, or null if empty. |
takeOwnership | takeOwnership overloads |
takeOwnershipIov | Take ownership of an iovec, turning it into an IOBuf. |
wrapBuffer | Create an IOBuf pointing to a buffer, without taking ownership. |
wrapBufferAsValue | wrapBufferAsValue overloads |
wrapIov | Convert an iovec array into an IOBuf. |
| Name | Description |
|---|---|
bser::decodePduLength | Determine how much data is needed to fully decode a BSER pdu. |
bser::parseBser | Parse a BSER value from an IOBuf with deserialization options. |
bser::parseBser | Parse a BSER value from an IOBuf. |
bser::toBserIOBuf | Serialize a dynamic value to a BSER-encoded IOBuf. |