diff options
Diffstat (limited to 'pw_stream/docs.rst')
-rw-r--r-- | pw_stream/docs.rst | 522 |
1 files changed, 143 insertions, 379 deletions
diff --git a/pw_stream/docs.rst b/pw_stream/docs.rst index a4aaeb913..a53f96d3d 100644 --- a/pw_stream/docs.rst +++ b/pw_stream/docs.rst @@ -5,7 +5,6 @@ ========= 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 @@ -22,13 +21,13 @@ 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); - } + 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()``. @@ -40,9 +39,9 @@ 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. +* 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 @@ -51,36 +50,36 @@ 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` + - ❌ + - ✅ -+-------------------------------------------+-----------------+------------------------------+ -| 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 ======================= @@ -89,295 +88,52 @@ 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. +.. doxygenclass:: pw::stream::Stream + :members: + :private-members: 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. +.. doxygenclass:: pw::stream::Reader + :members: - A Reader may or may not support seeking. Check seekable() or try calling - Seek() to determine if the stream is seekable. +.. doxygenclass:: pw::stream::SeekableReader + :members: -.. cpp:class:: SeekableReader : public RelativeSeekableReader +.. doxygenclass:: pw::stream::RelativeSeekableReader + :members: - 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. +.. doxygenclass:: pw::stream::NonSeekableReader + :members: 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. +.. doxygenclass:: pw::stream::Writer + :members: - Use in APIs when: - * Relative seeking is required. Usage in APIs should be rare; generally - Writer should be used instead. +.. doxygenclass:: pw::stream::SeekableWriter + :members: - Inherit from when: - * Implementing a Writer that can only support seeking near the current - position. +.. doxygenclass:: pw::stream::RelativeSeekableWriter + :members: - A buffered Writer that only supports seeking within its buffer is a good - example of a RelativeSeekableWriter. +.. doxygenclass:: pw::stream::NonSeekableWriter + :members: -.. 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. +.. doxygenclass:: pw::stream::ReaderWriter + :members: -.. cpp:class:: RelativeSeekableReaderWriter : public ReaderWriter +.. doxygenclass:: pw::stream::SeekableReaderWriter + :members: - 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. +.. doxygenclass:: pw::stream::RelativeSeekableReaderWriter + :members: - 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. +.. doxygenclass:: pw::stream::NonSeekableReaderWriter + :members: --------------- Implementations @@ -460,27 +216,27 @@ 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); - } + // 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); - } + // 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 =========================== @@ -497,26 +253,26 @@ 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); - } + // 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); - } + // 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 ======================= @@ -534,37 +290,37 @@ 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()); - } + 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(); + 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); - } + writer.Write(header); + return writer.Write(payload); + } ------------ Design notes @@ -582,11 +338,11 @@ 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. +* 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 =============== @@ -598,7 +354,7 @@ some similarities with Python's `io module 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 +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: @@ -654,17 +410,25 @@ 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` - -.. cpp:namespace-pop:: +* :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>`_. |