blob: a9b6aeb3d635befd18ebe84b5dcd21a23a544b90 [file] [edit]
.. _module-pw_buf:
======
pw_buf
======
.. pigweed-module::
:name: pw_buf
``pw_buf`` provides the :cc:`pw::Buf` and :cc:`pw::ConstBuf` classes.
------------------------
``Buf`` and ``ConstBuf``
------------------------
:cc:`pw::Buf` and :cc:`pw::ConstBuf` are views into contiguous blocks of owned
or unowned memory. The bytes in a ``Buf`` are mutable, while the bytes in a
``ConstBuf`` are read-only.
They can be interacted with like a ``std::span``, but offer
more functionality:
* **Automatic memory management:** Owned memory is automatically freed back
to its allocator when the ``Buf`` goes out of scope or is reset.
* **Slicing and reclaiming:** A region backing a ``Buf`` can be sliced into a
smaller region, creating a subspan view that can be passed along. These slices
can be later reclaimed. This can be useful for reserving headers and footers,
allowing someone else to populate the payload between.
Ownership and ``pw_allocator``
==============================
``Buf`` regions may optionally be allocated via a ``pw_allocator``, causing the
region's deallocator to travel with the ``Buf`` (and its slices) and
automatically reclaim the memory when it is destroyed.
Despite its name, ``pw_allocator`` does not necessarily mean "heap allocation".
It simply provides an interface for requesting and releasing ownership of a
block of memory. You can, for instance, create a ``pw_allocator`` implementation
that wraps a single static buffer with an "in use" flag; a ``Buf`` created over
it would just signal transfers of ownership without ever allocating any memory.
Slicing and reclaiming
======================
Both :cc:`pw::Buf` and :cc:`pw::ConstBuf` can be truncated or sliced.
A ``Buf`` can reclaim previously truncated or sliced regions.
For example, say you are implementing a simple framing protocol consisting of a
header followed by a payload. You would create a ``Buf`` large enough for both,
if possible directly over the memory region used by the transport (e.g. a DMA
buffer).
From this ``Buf``, you can then slice the size of the header from the front,
creating a new ``Buf`` that owns the full region, but only sees the payload.
This sliced ``Buf`` can be handed up to a higher layer for it to populate.
Depending on the type of protocol, you could handle this in one of two ways:
- If the packets have fixed size and parameters known up front, you can just
pre-populate the header before slicing the payload ``Buf``. Once the higher
layer is done, they can immediately hand it back to the transport as a
well-formed packet without further modifications.
- Alternatively, if some header fields depend on the payload (e.g. length,
checksum), you would reserve the header space upfront, hand the sliced ``Buf``
over, then have the higher layer return it to you. At that point, you would
reclaim the prefix span, inspect the written payload, and write the header
using the payload's finalized state.
Since ownership of the full underlying region travels with each slice, the
packet ``Buf`` can be safely destroyed at any point, returning its region back
to the allocator that provided it.
This process can be repeated multiple layers up, creating a full protocol stack
where each layer only knows about its own packet format, without ever copying
data between layers.
The ``pw_buf`` API provides utility functions for trimming and restoring views
of a buffer:
- :cc:`pw::Slice`: Shrinks a buffer view to a sub-range of its bytes.
- :cc:`pw::Truncate`: Truncates a buffer view to a smaller size from the start.
- :cc:`pw::Reclaim`: Expands a sliced buffer view back into its originally
allocated prefix and suffix bytes.
Relationship between Buf and ConstBuf
=====================================
:cc:`pw::Buf` and :cc:`pw::ConstBuf` manage owned or unowned buffer memory, but
serve different purposes:
* **Mutable vs. read-only:** A ``Buf`` provides mutable access to its bytes,
whereas a ``ConstBuf`` is strictly read-only.
* **Slicing and reclaiming:** A ``Buf`` can be sliced (:cc:`pw::Slice`) and
later reclaimed (:cc:`pw::Reclaim`), making it ideal for constructing write
packets where headers or footers are reserved and populated in stages. A
``ConstBuf`` only supports slicing and truncating, never reclaiming, which is
suited for read packets where layers strip headers as data moves up the
protocol stack without accessing outside their assigned slice.
A ``Buf`` can be converted to a ``ConstBuf`` by moving it:
.. literalinclude:: examples/allocate.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-move]
:end-before: // DOCSTAG: [pw_buf-examples-move]
Moving the ``Buf`` transfers ownership and leaves the ``Buf`` null. A ``Buf``
may also be used through a ``const ConstBuf&``, though accepting a
``pw::ConstByteSpan`` (or ``pw::span<const std::byte>``) by value is
recommended for functions that only borrow data for reading.
Examples
========
Allocate and TryAllocate a ``Buf``
----------------------------------
.. literalinclude:: examples/allocate.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-allocate]
:end-before: // DOCSTAG: [pw_buf-examples-allocate]
:linenos:
Create a ``Buf`` from a ``UniquePtr``
-------------------------------------
.. literalinclude:: examples/create_from_unique_ptr.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-unique_ptr]
:end-before: // DOCSTAG: [pw_buf-examples-unique_ptr]
:linenos:
Pass a ``Buf`` as a ``std::span``
---------------------------------
.. literalinclude:: examples/pass_as_span.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-span]
:end-before: // DOCSTAG: [pw_buf-examples-span]
:linenos:
Slice and Reclaim a ``Buf``
---------------------------
.. literalinclude:: examples/slice_and_reclaim.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-slice_and_reclaim]
:end-before: // DOCSTAG: [pw_buf-examples-slice_and_reclaim]
:linenos:
Using ``Buf`` in a simple network stack
---------------------------------------
The following snippet demonstrates how to use ``pw::Buf`` and ``pw::ConstBuf``
within a packet-oriented connection socket to implement a length-prefixed
protocol.
.. literalinclude:: examples/zero_copy_socket.cc
:language: c++
:start-after: // DOCSTAG: [pw_buf-examples-socket]
:end-before: // DOCSTAG: [pw_buf-examples-socket]
:linenos: