folly::IOBuf

IOBuf manages heap-allocated byte buffers.

Synopsis

Declared in <folly/io/IOBuf.h>

class IOBuf;

Description

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

Types

NameDescription
FillIovResult Result of filling an iovec array with IOBuf data.
Iterator Forward iterator over the buffers in an IOBuf chain.

Type Aliases

NameDescription
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.

Enums

NameDescription
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.

Member Functions

NameDescription
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.

Static Member Functions

NameDescription
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.

Non-Member Functions

NameDescription
bser::decodePduLengthDetermine how much data is needed to fully decode a BSER pdu.
bser::parseBserParse a BSER value from an IOBuf with deserialization options.
bser::parseBserParse a BSER value from an IOBuf.
bser::toBserIOBufSerialize a dynamic value to a BSER-encoded IOBuf.