aboutsummaryrefslogtreecommitdiff
path: root/pw_stream/docs.rst
blob: a4aaeb9135e6bcba92a292e9cdd3f0422d88692d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
.. _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
==============

+-------------------------------------------+-----------------+------------------------------+
| pw::stream Interfaces                     | Accept in APIs? | Extend to create new stream? |
+===========================================+=================+==============================+
| :cpp:class:`Stream`                       | ❌              | ❌                           |
+-------------------------------------------+-----------------+------------------------------+
| :cpp:class:`Reader`                       | ✅              | ❌                           |
|                                           |                 |                              |
| :cpp:class:`Writer`                       |                 |                              |
|                                           |                 |                              |
| :cpp:class:`ReaderWriter`                 |                 |                              |
+-------------------------------------------+-----------------+------------------------------+
| :cpp:class:`SeekableReader`               | ✅              | ✅                           |
|                                           |                 |                              |
| :cpp:class:`SeekableWriter`               |                 |                              |
|                                           |                 |                              |
| :cpp:class:`SeekableReaderWriter`         |                 |                              |
+-------------------------------------------+-----------------+------------------------------+
| :cpp:class:`RelativeSeekableReader`       | ✅ (rarely)     | ✅                           |
|                                           |                 |                              |
| :cpp:class:`RelativeSeekableWriter`       |                 |                              |
|                                           |                 |                              |
| :cpp:class:`RelativeSeekableReaderWriter` |                 |                              |
+-------------------------------------------+-----------------+------------------------------+
| :cpp:class:`NonSeekableReader`            | ❌              | ✅                           |
|                                           |                 |                              |
| :cpp:class:`NonSeekableWriter`            |                 |                              |
|                                           |                 |                              |
| :cpp:class:`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.

.. cpp:class:: Stream

  A generic stream that may support reading, writing, and seeking, but makes no
  guarantees about whether any operations are supported. Stream serves as the
  base for the Reader, Writer, and ReaderWriter interfaces.

  Stream cannot be extended directly. Instead, work with one of the derived
  classes that explicitly supports the required functionality. Stream should
  almost never be used in APIs; accept a derived class with the required
  capabilities instead.

  All Stream methods are blocking. They return when the requested operation
  completes.

  **Public methods**

  .. cpp:function:: bool readable() const

    True if :cpp:func:`Read` is supported.

  .. cpp:function:: bool writable() const

    True if :cpp:func:`Write` is supported.

  .. cpp:function:: bool seekable() const

    True if :cpp:func:`Seek` is supported.

  .. cpp:function:: Result<ByteSpan> Read(ByteSpan buffer)
  .. cpp:function:: Result<ByteSpan> Read(void* buffer, size_t size_bytes)

    Reads data from the stream into the provided buffer, if supported. As many
    bytes as are available up to the buffer size are copied into the buffer.
    Remaining bytes may by read in subsequent calls.

    Returns:

      * OK - Between 1 and dest.size_bytes() were successfully read. Returns
        the span of read bytes.
      * UNIMPLEMENTED - This stream does not support writing.
      * FAILED_PRECONDITION - The Reader  is not in state to read data.
      * RESOURCE_EXHAUSTED - Unable to read any bytes at this time. No bytes
        read. Try again once bytes become available.
      * OUT_OF_RANGE - Reader has been exhausted, similar to EOF. No bytes were
        read, no more will be read.

  .. cpp:function:: Status Write(ConstByteSpan data)

    Writes the provided data to the stream, if supported.

    Returns:

      * OK - Data was successfully accepted by the stream.
      * UNIMPLEMENTED - This stream does not support writing.
      * FAILED_PRECONDITION - The writer is not in a state to accept data.
      * RESOURCE_EXHAUSTED - The writer was unable to write all of requested data
        at this time. No data was written.
      * OUT_OF_RANGE - The Writer has been exhausted, similar to EOF. No data was
        written; no more will be written.


  .. cpp:function:: Status Seek(ptrdiff_t offset, Whence origin = kBeginning)

    Changes the current read & write position in the stream, if supported.

    Returns:

      * OK - Successfully updated the position.
      * UNIMPLEMENTED - Seeking is not supported for this stream.
      * OUT_OF_RANGE - Attempted to seek beyond the bounds of the stream. The
        position is unchanged.

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

    Returns the current read & write position in the stream, if supported.
    Returns ``Stream::kUnknownPosition`` (``size_t(-1)``) if unsupported.

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

    Likely minimum bytes available to read. Returns ``kUnlimited``
    (``size_t(-1)``) if there is no limit or it is unknown.

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

    Likely minimum bytes available to write. Returns ``kUnlimited``
    (``size_t(-1)``) if there is no limit or it is unknown.

  **Private virtual methods**

  Stream's public methods are non-virtual. The public methods call private
  virtual methods that are implemented by derived classes.

  .. cpp:function:: private virtual StatusWithSize DoRead(ByteSpan destination)

    Virtual :cpp:func:`Read` function implemented by derived classes.

  .. cpp:function:: private virtual Status DoWrite(ConstByteSpan data)

    Virtual :cpp:func:`Write` function implemented by derived classes.

  .. cpp:function:: private virtual Status DoSeek(ptrdiff_t offset, Whence origin)

    Virtual :cpp:func:`Seek` function implemented by derived classes.

  .. cpp:function:: private virtual size_t DoTell() const

    Virtual :cpp:func:`Tell` function optionally implemented by derived classes.
    The default implementation always returns ``kUnknownPosition``.

  .. cpp:function:: private virtual size_t ConservativeLimit(LimitType limit_type)

    Virtual function optionally implemented by derived classes that is used for
    :cpp:func:`ConservativeReadLimit` and :cpp:func:`ConservativeWriteLimit`.
    The default implementation returns ``kUnlimited`` or ``0`` depending on
    whether the stream is readable/writable.

Reader interfaces
-----------------
.. cpp:class:: Reader : public Stream

   A Stream that supports reading but not writing. The Write() method is hidden.

   Use in APIs when:
     * Must read from, but not write to, a stream.
     * May or may not need seeking. Use a SeekableReader& if seeking is
       required.

   Inherit from when:
     * Reader cannot be extended directly. Instead, extend SeekableReader,
       NonSeekableReader, or (rarely) RelativeSeekableReader, as appropriate.

   A Reader may or may not support seeking. Check seekable() or try calling
   Seek() to determine if the stream is seekable.

.. cpp:class:: SeekableReader : public RelativeSeekableReader

   A Reader that fully supports seeking.

   Use in APIs when:
     * Absolute seeking is required. Use Reader& if seeking is not required or
       seek failures can be handled gracefully.

   Inherit from when:
     * Implementing a reader that supports absolute seeking.

.. cpp:class:: RelativeSeekableReader : public Reader

   A Reader that at least partially supports seeking. Seeking within some range
   of the current position works, but seeking beyond that or from other origins
   may or may not be supported. The extent to which seeking is possible is NOT
   exposed by this API.

   Use in APIs when:
     * Relative seeking is required. Usage in APIs should be rare; generally
       Reader should be used instead.

   Inherit from when:
     * Implementing a Reader that can only support seeking near the current
       position.

   A buffered Reader that only supports seeking within its buffer is a good
   example of a RelativeSeekableReader.

.. cpp:class:: NonSeekableReader : public Reader

   A Reader that does not support seeking. The Seek() method is hidden.

   Use in APIs when:
     * Do NOT use in APIs! If seeking is not required, use Reader& instead.

   Inherit from when:
     * Implementing a Reader that does not support seeking.

Writer interfaces
-----------------
.. cpp:class:: Writer : public Stream

   A Stream that supports writing but not reading. The Read() method is hidden.

   Use in APIs when:
     * Must write to, but not read from, a stream.
     * May or may not need seeking. Use a SeekableWriter& if seeking is
       required.

   Inherit from when:
     * Writer cannot be extended directly. Instead, extend SeekableWriter,
       NonSeekableWriter, or (rarely) RelativeSeekableWriter, as appropriate.

   A Writer may or may not support seeking. Check seekable() or try calling
   Seek() to determine if the stream is seekable.

.. cpp:class:: SeekableWriter : public RelativeSeekableWriter

   A Writer that fully supports seeking.

   Use in APIs when:
     * Absolute seeking is required. Use Writer& if seeking is not required or
       seek failures can be handled gracefully.

   Inherit from when:
     * Implementing a writer that supports absolute seeking.


.. cpp:class:: RelativeSeekableWriter : public Writer

   A Writer that at least partially supports seeking. Seeking within some range
   of the current position works, but seeking beyond that or from other origins
   may or may not be supported. The extent to which seeking is possible is NOT
   exposed by this API.

   Use in APIs when:
     * Relative seeking is required. Usage in APIs should be rare; generally
       Writer should be used instead.

   Inherit from when:
     * Implementing a Writer that can only support seeking near the current
       position.

   A buffered Writer that only supports seeking within its buffer is a good
   example of a RelativeSeekableWriter.

.. cpp:class:: NonSeekableWriter : public Writer

   A Writer that does not support seeking. The Seek() method is hidden.

   Use in APIs when:
     * Do NOT use in APIs! If seeking is not required, use Writer& instead.

   Inherit from when:
     * Implementing a Writer that does not support seeking.

ReaderWriter interfaces
-----------------------
.. cpp:class:: ReaderWriter : public Stream

   A Stream that supports both reading and writing.

   Use in APIs when:
     * Must both read from and write to a stream.
     * May or may not need seeking. Use a SeekableReaderWriter& if seeking is
       required.

   Inherit from when:
     * Cannot extend ReaderWriter directly. Instead, extend
       SeekableReaderWriter, NonSeekableReaderWriter, or (rarely)
       RelativeSeekableReaderWriter, as appropriate.

   A ReaderWriter may or may not support seeking. Check seekable() or try
   calling Seek() to determine if the stream is seekable.

.. cpp:class:: SeekableReaderWriter : public RelativeSeekableReaderWriter

   A ReaderWriter that fully supports seeking.

   Use in APIs when:
     * Absolute seeking is required. Use ReaderWriter& if seeking is not
       required or seek failures can be handled gracefully.

   Inherit from when:
     * Implementing a writer that supports absolute seeking.

.. cpp:class:: RelativeSeekableReaderWriter : public ReaderWriter

   A ReaderWriter that at least partially supports seeking. Seeking within some
   range of the current position works, but seeking beyond that or from other
   origins may or may not be supported. The extent to which seeking is possible
   is NOT exposed by this API.

   Use in APIs when:
     * Relative seeking is required. Usage in APIs should be rare; generally
       ReaderWriter should be used instead.

   Inherit from when:
     * Implementing a ReaderWriter that can only support seeking near the
       current position.

   A buffered ReaderWriter that only supports seeking within its buffer is a
   good example of a RelativeSeekableReaderWriter.

.. cpp:class:: NonSeekableReaderWriter : public ReaderWriter

   A ReaderWriter that does not support seeking. The Seek() method is hidden.

   Use in APIs when:
     * Do NOT use in APIs! If seeking is not required, use ReaderWriter&
       instead.

   Inherit from when:
     * Implementing a ReaderWriter that does not support seeking.

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

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
<https://pkg.go.dev/io package>`_ 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.

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

.. cpp:namespace-pop::

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