.. _module-pw_stream:

.. cpp:namespace-push:: pw::stream

=========
pw_stream
=========
``pw_stream`` provides a foundational interface for streaming data from one part
of a system to another. In the simplest use cases, this is basically a memcpy
behind a reusable interface that can be passed around the system. On the other
hand, the flexibility of this interface means a ``pw_stream`` could terminate is
something more complex, like a UART stream or flash memory.

--------
Overview
--------
At the most basic level, ``pw_stream``'s interfaces provide very simple handles
to enabling streaming data from one location in a system to an endpoint.

Example:

.. code-block:: cpp

   Status DumpSensorData(pw::stream::Writer& writer) {
     static char temp[64];
     ImuSample imu_sample;
     imu.GetSample(&info);
     size_t bytes_written = imu_sample.AsCsv(temp, sizeof(temp));
     return writer.Write(temp, bytes_written);
   }

In this example, ``DumpSensorData()`` only cares that it has access to a
:cpp:class:`Writer` that it can use to stream data to using ``Writer::Write()``.
The :cpp:class:`Writer` itself can be backed by anything that can act as a data
"sink."

---------------------
pw::stream Interfaces
---------------------
There are three basic capabilities of a stream:

* Reading -- Bytes can be read from the stream.
* Writing -- Bytes can be written to the stream.
* Seeking -- The position in the stream can be changed.

``pw_stream`` provides a family of stream classes with different capabilities.
The most basic class, :cpp:class:`Stream` guarantees no functionality, while the
most capable class, :cpp:class:`SeekableReaderWriter` supports reading, writing,
and seeking.

Usage overview
==============
.. list-table::
   :header-rows: 1

   * - pw::stream Interfaces
     - Accept in APIs?
     - Extend to create new stream?
   * - :cpp:class:`pw::stream::Stream`
     - ❌
     - ❌
   * - | :cpp:class:`pw::stream::Reader`
       | :cpp:class:`pw::stream::Writer`
       | :cpp:class:`pw::stream::ReaderWriter`
     - ✅
     - ❌
   * - | :cpp:class:`pw::stream::SeekableReader`
       | :cpp:class:`pw::stream::SeekableWriter`
       | :cpp:class:`pw::stream::SeekableReaderWriter`
     - ✅
     - ✅
   * - | :cpp:class:`pw::stream::RelativeSeekableReader`
       | :cpp:class:`pw::stream::RelativeSeekableWriter`
       | :cpp:class:`pw::stream::RelativeSeekableReaderWriter`
     - ✅ (rarely)
     - ✅
   * - | :cpp:class:`pw::stream::NonSeekableReader`
       | :cpp:class:`pw::stream::NonSeekableWriter`
       | :cpp:class:`pw::stream::NonSeekableReaderWriter`
     - ❌
     - ✅


Interface documentation
=======================
Summary documentation for the ``pw_stream`` interfaces is below. See the API
comments in `pw_stream/public/pw_stream/stream.h
<https://cs.pigweed.dev/pigweed/+/main:pw_stream/public/pw_stream/stream.h>`_
for full details.

.. doxygenclass:: pw::stream::Stream
   :members:
   :private-members:

Reader interfaces
-----------------
.. doxygenclass:: pw::stream::Reader
   :members:

.. doxygenclass:: pw::stream::SeekableReader
   :members:

.. doxygenclass:: pw::stream::RelativeSeekableReader
   :members:

.. doxygenclass:: pw::stream::NonSeekableReader
   :members:

Writer interfaces
-----------------
.. doxygenclass:: pw::stream::Writer
   :members:

.. doxygenclass:: pw::stream::SeekableWriter
   :members:

.. doxygenclass:: pw::stream::RelativeSeekableWriter
   :members:

.. doxygenclass:: pw::stream::NonSeekableWriter
   :members:


ReaderWriter interfaces
-----------------------
.. doxygenclass:: pw::stream::ReaderWriter
   :members:

.. doxygenclass:: pw::stream::SeekableReaderWriter
   :members:

.. doxygenclass:: pw::stream::RelativeSeekableReaderWriter
   :members:

.. doxygenclass:: pw::stream::NonSeekableReaderWriter
   :members:

---------------
Implementations
---------------
``pw_stream`` includes a few stream implementations for general use.

.. cpp:class:: MemoryWriter : public SeekableWriter

  The ``MemoryWriter`` class implements the :cpp:class:`Writer` interface by
  backing the data destination with an **externally-provided** memory buffer.
  ``MemoryWriterBuffer`` extends ``MemoryWriter`` to internally provide a memory
  buffer.

  The ``MemoryWriter`` can be accessed like a standard C++ container. The
  contents grow as data is written.

.. cpp:class:: MemoryReader : public SeekableReader

  The ``MemoryReader`` class implements the :cpp:class:`Reader` interface by
  backing the data source with an **externally-provided** memory buffer.

.. cpp:class:: NullStream : public SeekableReaderWriter

  ``NullStream`` is a no-op stream implementation, similar to ``/dev/null``.
  Writes are always dropped. Reads always return ``OUT_OF_RANGE``. Seeks have no
  effect.

.. cpp:class:: CountingNullStream : public SeekableReaderWriter

  ``CountingNullStream`` is a no-op stream implementation, like
  :cpp:class:`NullStream`, that counts the number of bytes written.

  .. cpp:function:: size_t bytes_written() const

    Returns the number of bytes provided to previous ``Write()`` calls.

.. cpp:class:: StdFileWriter : public SeekableWriter

  ``StdFileWriter`` wraps an ``std::ofstream`` with the :cpp:class:`Writer`
  interface.

.. cpp:class:: StdFileReader : public SeekableReader

  ``StdFileReader`` wraps an ``std::ifstream`` with the :cpp:class:`Reader`
  interface.

.. cpp:class:: SocketStream : public NonSeekableReaderWriter

  ``SocketStream`` wraps posix-style TCP sockets with the :cpp:class:`Reader`
  and :cpp:class:`Writer` interfaces. It can be used to connect to a TCP server,
  or to communicate with a client via the ``ServerSocket`` class.

.. cpp:class:: ServerSocket

  ``ServerSocket`` wraps a posix server socket, and produces a
  :cpp:class:`SocketStream` for each accepted client connection.

------------------
Why use pw_stream?
------------------

Standard API
============
``pw_stream`` provides a standard way for classes to express that they have the
ability to write data. Writing to one sink versus another sink is a matter of
just passing a reference to the appropriate :cpp:class:`Writer`.

As an example, imagine dumping sensor data. If written against a random HAL
or one-off class, there's porting work required to write to a different sink
(imagine writing over UART vs dumping to flash memory). Building a "dumping"
implementation against the :cpp:class:`Writer` interface prevents a dependency
on a bespoke API that would require porting work.

Similarly, after building a :cpp:class:`Writer` implementation for a Sink that
data could be dumped to, that same :cpp:class:`Writer` can be reused for other
contexts that already write data to the :cpp:class:`pw::stream::Writer`
interface.

Before:

.. code-block:: cpp

   // Not reusable, depends on `Uart`.
   void DumpSensorData(Uart& uart) {
     static char temp[64];
     ImuSample imu_sample;
     imu.GetSample(&info);
     size_t bytes_written = imu_sample.AsCsv(temp, sizeof(temp));
     uart.Transmit(temp, bytes_written, /*timeout_ms=*/ 200);
   }

After:

.. code-block:: cpp

   // Reusable; no more Uart dependency!
   Status DumpSensorData(Writer& writer) {
     static char temp[64];
     ImuSample imu_sample;
     imu.GetSample(&info);
     size_t bytes_written = imu_sample.AsCsv(temp, sizeof(temp));
     return writer.Write(temp, bytes_written);
   }

Reduce intermediate buffers
===========================
Often functions that write larger blobs of data request a buffer is passed as
the destination that data should be written to. This *requires* a buffer to be
allocated, even if the data only exists in that buffer for a very short period
of time before it's written somewhere else.

In situations where data read from somewhere will immediately be written
somewhere else, a :cpp:class:`Writer` interface can cut out the middleman
buffer.

Before:

.. code-block:: cpp

   // Requires an intermediate buffer to write the data as CSV.
   void DumpSensorData(Uart& uart) {
     char temp[64];
     ImuSample imu_sample;
     imu.GetSample(&info);
     size_t bytes_written = imu_sample.AsCsv(temp, sizeof(temp));
     uart.Transmit(temp, bytes_written, /*timeout_ms=*/ 200);
   }

After:

.. code-block:: cpp

   // Both DumpSensorData() and RawSample::AsCsv() use a Writer, eliminating the
   // need for an intermediate buffer.
   Status DumpSensorData(Writer& writer) {
     RawSample imu_sample;
     imu.GetSample(&info);
     return imu_sample.AsCsv(writer);
   }

Prevent buffer overflow
=======================
When copying data from one buffer to another, there must be checks to ensure the
copy does not overflow the destination buffer. As this sort of logic is
duplicated throughout a codebase, there's more opportunities for bound-checking
bugs to sneak in. ``Writers`` manage this logic internally rather than pushing
the bounds checking to the code that is moving or writing the data.

Similarly, since only the :cpp:class:`Writer` has access to any underlying
buffers, it's harder for functions that share a :cpp:class:`Writer` to
accidentally clobber data written by others using the same buffer.

Before:

.. code-block:: cpp

   Status BuildPacket(Id dest, span<const std::byte> payload,
                      span<std::byte> dest) {
     Header header;
     if (dest.size_bytes() + payload.size_bytes() < sizeof(Header)) {
       return Status::ResourceExhausted();
     }
     header.dest = dest;
     header.src = DeviceId();
     header.payload_size = payload.size_bytes();

     memcpy(dest.data(), &header, sizeof(header));
     // Forgetting this line would clobber buffer contents. Also, using
     // a temporary span instead could leave `dest` to be misused elsewhere in
     // the function.
     dest = dest.subspan(sizeof(header));
     memcpy(dest.data(), payload.data(), payload.size_bytes());
   }

After:

.. code-block:: cpp

   Status BuildPacket(Id dest, span<const std::byte> payload, Writer& writer) {
     Header header;
     header.dest = dest;
     header.src = DeviceId();
     header.payload_size = payload.size_bytes();

     writer.Write(header);
     return writer.Write(payload);
   }

------------
Design notes
------------

Sync & Flush
============
The :cpp:class:`pw::stream::Stream` API does not include ``Sync()`` or
``Flush()`` functions. There no mechanism in the :cpp:class:`Stream` API to
synchronize a :cpp:class:`Reader`'s potentially buffered input with its
underlying data source. This must be handled by the implementation if required.
Similarly, the :cpp:class:`Writer` implementation is responsible for flushing
any buffered data to the sink.

``Flush()`` and ``Sync()`` were excluded from :cpp:class:`Stream` for a few
reasons:

* The semantics of when to call ``Flush()``/``Sync()`` on the stream are
  unclear. The presence of these methods complicates using a :cpp:class:`Reader`
  or :cpp:class:`Writer`.
* Adding one or two additional virtual calls increases the size of all
  :cpp:class:`Stream` vtables.

.. _module-pw_stream-class-hierarchy:

Class hierarchy
===============
All ``pw_stream`` classes inherit from a single, common base with all possible
functionality: :cpp:class:`pw::stream::Stream`. This structure has
some similarities with Python's `io module
<https://docs.python.org/3/library/io.html>`_ and C#'s `Stream class
<https://docs.microsoft.com/en-us/dotnet/api/system.io.stream>`_.

An alternative approach is to have the reading, writing, and seeking portions of
the interface provided by different entities. This is how Go's `io
package <https://pkg.go.dev/io>`_ and C++'s `input/output library
<https://en.cppreference.com/w/cpp/io>`_ are structured.

We chose to use a single base class for a few reasons:

* The inheritance hierarchy is simple and linear. Despite the linear
  hierarchy, combining capabilities is natural with classes like
  :cpp:class:`ReaderWriter`.

  In C++, separate interfaces for each capability requires either a complex
  virtual inheritance hierarchy or entirely separate hierarchies for each
  capability. Separate hierarchies can become cumbersome when trying to
  combine multiple capabilities. A :cpp:class:`SeekableReaderWriter` would
  have to implement three different interfaces, which means three different
  vtables and three vtable pointers in each instance.
* Stream capabilities are clearly expressed in the type system, while
  naturally supporting optional functionality. A :cpp:class:`Reader` may
  or may not support :cpp:func:`Stream::Seek`. Applications that can handle
  seek failures gracefully way use seek on any :cpp:class:`Reader`. If seeking
  is strictly necessary, an API can accept a :cpp:class:`SeekableReader`
  instead.

  Expressing optional functionality in the type system is cumbersome when
  there are distinct interfaces for each capability. ``Reader``, ``Writer``,
  and ``Seeker`` interfaces would not be sufficient. To match the flexibility
  of the current structure, there would have to be separate optional versions
  of each interface, and classes for various combinations. :cpp:class:`Stream`
  would be an "OptionalReaderOptionalWriterOptionalSeeker" in this model.
* Code reuse is maximized. For example, a single
  :cpp:func:`Stream::ConservativeLimit` implementation supports many stream
  implementations.

Virtual interfaces
==================
``pw_stream`` uses virtual functions. Virtual functions enable runtime
polymorphism. The same code can be used with any stream implementation.

Virtual functions have inherently has more overhead than a regular function
call. However, this is true of any polymorphic API. Using a C-style ``struct``
of function pointers makes different trade-offs but still has more overhead than
a regular function call.

For many use cases, the overhead of virtual calls insignificant. However, in
some extremely performance-sensitive contexts, the flexibility of the virtual
interface may not justify the performance cost.

Asynchronous APIs
=================
At present, ``pw_stream`` is synchronous. All :cpp:class:`Stream` API calls are
expected to block until the operation is complete. This might be undesirable
for slow operations, like writing to NOR flash.

Pigweed has not yet established a pattern for asynchronous C++ APIs. The
:cpp:class:`Stream` class may be extended in the future to add asynchronous
capabilities, or a separate ``AsyncStream`` could be created.

.. cpp:namespace-pop::

------------
Dependencies
------------
* :ref:`module-pw_assert`
* :ref:`module-pw_preprocessor`
* :ref:`module-pw_status`
* :ref:`module-pw_span`

------
Zephyr
------
To enable ``pw_stream`` for Zephyr add ``CONFIG_PIGWEED_STREAM=y`` to the
project's configuration.

----
Rust
----
Pigweed centric analogs to Rust ``std``'s ``Read``, ``Write``, ``Seek`` traits
as well as a basic ``Cursor`` implementation are provided by the
`pw_stream crate </rustdoc/pw_stream>`_.


.. toctree::
   :hidden:
   :maxdepth: 1

   Backends <backends>
