xref: /aosp_15_r20/external/pigweed/pw_stream/public/pw_stream/stream.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <array>
17 #include <cstddef>
18 #include <cstdint>
19 #include <limits>
20 
21 #include "pw_assert/assert.h"
22 #include "pw_bytes/span.h"
23 #include "pw_result/result.h"
24 #include "pw_span/span.h"
25 #include "pw_status/status.h"
26 #include "pw_status/status_with_size.h"
27 #include "pw_toolchain/internal/sibling_cast.h"
28 
29 namespace pw::stream {
30 
31 /// A generic stream that may support reading, writing, and seeking, but makes
32 /// no guarantees about whether any operations are supported. Unsupported
33 /// functions return Status::Unimplemented() Stream serves as the base for the
34 /// Reader, Writer, and ReaderWriter interfaces.
35 ///
36 /// Stream cannot be extended directly. Instead, work with one of the derived
37 /// classes that explicitly supports the required functionality. Stream should
38 /// almost never be used in APIs; accept a derived class with the required
39 /// capabilities instead.
40 ///
41 /// All Stream methods are blocking. They return when the requested operation
42 /// completes.
43 class Stream {
44  public:
45   /// Positions from which to seek.
46   enum Whence : uint8_t {
47     /// Seek from the beginning of the stream. The offset is a direct offset
48     /// into the data.
49     kBeginning = 0b001,
50 
51     /// Seek from the current position in the stream. The offset is added to the
52     /// current position. Use a negative offset to seek backwards.
53     ///
54     /// Implementations may only support seeking within a limited range from the
55     /// current position.
56     kCurrent = 0b010,
57 
58     /// Seek from the end of the stream. The offset is added to the end
59     /// position. Use a negative offset to seek backwards from the end.
60     kEnd = 0b100,
61   };
62 
63   /// Value returned from read/write limit if unlimited.
64   static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max();
65 
66   /// Returned by Tell() if getting the position is not supported.
67   static constexpr size_t kUnknownPosition = std::numeric_limits<size_t>::max();
68 
69   virtual ~Stream() = default;
70 
71   /// @retval True If reading is supported.
72   /// @retval False If Read() returns UNIMPLEMENTED.
readable()73   constexpr bool readable() const { return readable_; }
74 
75   /// @retval True If writing is supported.
76   /// @retval False If Write() returns UNIMPLEMENTED.
writable()77   constexpr bool writable() const { return writable_; }
78 
79   /// @retval True If the stream supports seeking.
80   /// @retval False If the stream does not supports seeking.
seekable()81   constexpr bool seekable() const { return seekability_ != Seekability::kNone; }
82 
83   /// True if the stream supports seeking from the specified origin.
seekable(Whence origin)84   constexpr bool seekable(Whence origin) const {
85     return (static_cast<uint8_t>(seekability_) & origin) != 0u;
86   }
87 
88   /// Reads data from the stream into the provided buffer, if supported. As many
89   /// bytes as are available up to the buffer size are copied into the buffer.
90   /// Remaining bytes may by read in subsequent calls. If any number of bytes
91   /// are read returns OK with a span of the bytes read.
92   ///
93   /// If the reader has been exhausted and is and can no longer read additional
94   /// bytes it will return `OUT_OF_RANGE`. This is similar to end-of-file (EOF).
95   /// Read will only return `OUT_OF_RANGE` if ConservativeReadLimit() is and
96   /// will remain zero. A Read operation that is successful and also exhausts
97   /// the reader returns OK, with all following calls returning `OUT_OF_RANGE`.
98   ///
99   /// Derived classes should NOT try to override these public read methods.
100   /// Instead, provide an implementation by overriding DoRead().
101   ///
102   /// @returns @rst
103   ///
104   /// .. pw-status-codes::
105   ///
106   ///    OK: Between 1 and ``dest.size_bytes()`` were successfully
107   ///    read. Returns the span of read bytes.
108   ///
109   ///    UNIMPLEMENTED: This stream does not support reading.
110   ///
111   ///    FAILED_PRECONDITION: The Reader is not in state to read data.
112   ///
113   ///    RESOURCE_EXHAUSTED: Unable to read any bytes at this time. No
114   ///    bytes read. Try again once bytes become available.
115   ///
116   ///    OUT_OF_RANGE: Reader has been exhausted, similar to EOF. No bytes
117   ///    were read, no more will be read.
118   ///
119   /// @endrst
Read(ByteSpan dest)120   Result<ByteSpan> Read(ByteSpan dest) {
121     PW_DASSERT(dest.empty() || dest.data() != nullptr);
122     StatusWithSize result = DoRead(dest);
123 
124     if (result.ok()) {
125       return dest.first(result.size());
126     }
127     return result.status();
128   }
129   /// @overload
Read(void * dest,size_t size_bytes)130   Result<ByteSpan> Read(void* dest, size_t size_bytes) {
131     return Read(span(static_cast<std::byte*>(dest), size_bytes));
132   }
133 
134   /// Reads exactly the number of bytes requested into the provided buffer, if
135   /// supported. Internally, the stream is read as many times as necessary to
136   /// fill the destination buffer. If fewer bytes than requested are available,
137   /// `OUT_OF_RANGE` is returned.
138   ///
139   /// Other errors may be returned, as documented on Read().
ReadExact(ByteSpan const buffer)140   Result<ByteSpan> ReadExact(ByteSpan const buffer) {
141     ByteSpan dest = buffer;
142     while (!dest.empty()) {
143       auto result = Read(dest);
144       if (!result.ok()) {
145         return result.status();
146       }
147       const size_t bytes_read = result->size();
148       PW_DASSERT(bytes_read != 0);  // Should never happen, according to API.
149       dest = dest.subspan(bytes_read);
150     }
151     return buffer;
152   }
153 
154   /// Writes data to this stream. Data is not guaranteed to be fully written out
155   /// to final resting place on Write return.
156   ///
157   /// If the writer is unable to fully accept the input data size it will abort
158   /// the write and return `RESOURCE_EXHAUSTED`.
159   ///
160   /// If the writer has been exhausted and is and can no longer accept
161   /// additional bytes it will return `OUT_OF_RANGE`. This is similar to
162   /// end-of-file (EOF).  Write will only return `OUT_OF_RANGE` if
163   /// ConservativeWriteLimit() is and will remain zero. A Write operation that
164   /// is successful and also exhausts the writer returns OK, with all following
165   /// calls returning `OUT_OF_RANGE`. When ConservativeWriteLimit() is greater
166   /// than zero, a Write that is a number of bytes beyond what will exhaust the
167   /// Write will abort and return `RESOURCE_EXHAUSTED` rather than OUT_OF_RANGE
168   /// because the writer is still able to write bytes.
169   ///
170   /// Derived classes should NOT try to override the public Write methods.
171   /// Instead, provide an implementation by overriding DoWrite().
172   ///
173   /// @returns @rst
174   ///
175   /// .. pw-status-codes::
176   ///
177   ///    OK: Data was successfully accepted by the stream.
178   ///
179   ///    UNIMPLEMENTED: This stream does not support writing.
180   ///
181   ///    FAILED_PRECONDITION: The writer is not in a state to accept data.
182   ///
183   ///    RESOURCE_EXHAUSTED: The writer was unable to write all of requested
184   ///    data at this time. No data was written.
185   ///
186   ///    OUT_OF_RANGE: The Writer has been exhausted, similar to EOF. No
187   ///    data was written; no more will be written.
188   ///
189   /// @endrst
Write(ConstByteSpan data)190   Status Write(ConstByteSpan data) {
191     PW_DASSERT(data.empty() || data.data() != nullptr);
192     return DoWrite(data);
193   }
194   /// @overload
Write(const void * data,size_t size_bytes)195   Status Write(const void* data, size_t size_bytes) {
196     return Write(span(static_cast<const std::byte*>(data), size_bytes));
197   }
198   /// @overload
Write(const std::byte b)199   Status Write(const std::byte b) { return Write(&b, 1); }
200 
201   /// Changes the current position in the stream for both reading and writing,
202   /// if supported.
203   ///
204   /// Seeking to a negative offset is invalid. The behavior when seeking beyond
205   /// the end of a stream is determined by the implementation. The
206   /// implementation could fail with OUT_OF_RANGE or append bytes to the stream.
207   ///
208   /// @returns @rst
209   ///
210   /// .. pw-status-codes::
211   ///
212   ///    OK: Successfully updated the position.
213   ///
214   ///    UNIMPLEMENTED: Seeking is not supported for this stream.
215   ///
216   ///    OUT_OF_RANGE: Attempted to seek beyond the bounds of the
217   ///    stream. The position is unchanged.
218   ///
219   /// @endrst
220   Status Seek(ptrdiff_t offset, Whence origin = kBeginning) {
221     return DoSeek(offset, origin);
222   }
223 
224   /// Returns the current position in the stream, if supported. The position is
225   /// the offset from the beginning of the stream. Returns
226   /// Stream::kUnknownPosition (`size_t(-1)`) if the position is unknown.
227   ///
228   /// Streams that support seeking from the beginning always support Tell().
229   /// Other streams may or may not support Tell().
Tell()230   size_t Tell() { return DoTell(); }
231 
232   /// Liklely (not guaranteed) minimum bytes available to read at this time.
233   /// This number is advisory and not guaranteed to read full number of
234   /// requested bytes or without a `RESOURCE_EXHAUSTED` or `OUT_OF_RANGE`. As
235   /// Reader processes/handles/receives enqueued data or other contexts read
236   /// data this number can go up or down for some Readers.
237   ///
238   /// @retval zero if, in the current state, Read() would not return OkStatus().
239   /// @retval kUnlimited if the implementation imposes no limits on read sizes.
ConservativeReadLimit()240   size_t ConservativeReadLimit() const {
241     return ConservativeLimit(LimitType::kRead);
242   }
243 
244   /// Likely (not guaranteed) minimum bytes available to write at this time.
245   /// This number is advisory and not guaranteed to write without a
246   /// `RESOURCE_EXHAUSTED` or `OUT_OF_RANGE`. As Writer processes/handles
247   /// enqueued of other contexts write data this number can go up or down for
248   /// some Writers.  Returns zero if, in the current state, Write() would not
249   /// return OkStatus().
250   ///
251   /// Returns kUnlimited if the implementation has no limits on write sizes.
ConservativeWriteLimit()252   size_t ConservativeWriteLimit() const {
253     return ConservativeLimit(LimitType::kWrite);
254   }
255 
256  protected:
257   // Used to indicate the type of limit being queried in ConservativeLimit.
258   enum class LimitType : bool { kRead, kWrite };
259 
260  private:
261   // The Stream class should not be extended directly, so its constructor is
262   // private. To implement a new Stream, extend one of its derived classes.
263   friend class Reader;
264   friend class RelativeSeekableReader;
265   friend class SeekableReader;
266   friend class NonSeekableReader;
267 
268   friend class Writer;
269   friend class RelativeSeekableWriter;
270   friend class SeekableWriter;
271   friend class NonSeekableWriter;
272 
273   friend class ReaderWriter;
274   friend class RelativeSeekableReaderWriter;
275   friend class SeekableReaderWriter;
276   friend class NonSeekableReaderWriter;
277 
278   /// Seekability expresses the origins from which the stream always supports
279   /// seeking. Seeking from other origins may work, but is not guaranteed.
280   ///
281   /// Seekability is implemented as a bitfield of Whence values.
282   enum class Seekability : uint8_t {
283     /// No type of seeking is supported.
284     kNone = 0,
285 
286     /// Seeking from the current position is supported, but the range may be
287     /// limited. For example, a buffered stream might support seeking within the
288     /// buffered data, but not before or after.
289     kRelative = kCurrent,
290 
291     /// The stream supports random access anywhere within the stream.
292     kAbsolute = kBeginning | kCurrent | kEnd,
293   };
294 
295   // These are the virtual methods that are implemented by derived classes. The
296   // public API functions call these virtual functions.
297 
Stream(bool readable,bool writable,Seekability seekability)298   constexpr Stream(bool readable, bool writable, Seekability seekability)
299       : readable_(readable), writable_(writable), seekability_(seekability) {}
300 
301   /// Virtual Read() function implemented by derived classes.
302   virtual StatusWithSize DoRead(ByteSpan destination) = 0;
303 
304   /// Virtual Write() function implemented by derived classes.
305   virtual Status DoWrite(ConstByteSpan data) = 0;
306 
307   /// Virtual Seek() function implemented by derived classes.
308   virtual Status DoSeek(ptrdiff_t offset, Whence origin) = 0;
309 
310   /// Virtual Tell() function optionally implemented by derived classes.
311   /// The default implementation always returns kUnknownPosition.
DoTell()312   virtual size_t DoTell() { return kUnknownPosition; }
313 
314   /// Virtual function optionally implemented by derived classes that is used
315   /// for ConservativeReadLimit() and ConservativeWriteLimit().
316   ///
317   /// The default implementation returns kUnlimited or ``0`` depending on
318   /// whether the stream is readable/writable.
ConservativeLimit(LimitType limit_type)319   virtual size_t ConservativeLimit(LimitType limit_type) const {
320     if (limit_type == LimitType::kRead) {
321       return readable() ? kUnlimited : 0;
322     }
323     return writable() ? kUnlimited : 0;
324   }
325 
326   bool readable_;
327   bool writable_;
328   Seekability seekability_;
329 };
330 
331 /// A Stream that supports reading but not writing. The Write() method is
332 /// hidden.
333 ///
334 /// Use in APIs when:
335 ///   * Must read from, but not write to, a stream.
336 ///   * May or may not need seeking. Use a SeekableReader& if seeking is
337 ///     required.
338 ///
339 /// Inherit from when:
340 ///   * Reader cannot be extended directly. Instead, extend SeekableReader,
341 ///     NonSeekableReader, or (rarely) RelativeSeekableReader, as appropriate.
342 ///
343 /// A Reader may or may not support seeking. Check seekable() or try calling
344 /// Seek() to determine if the stream is seekable.
345 class Reader : public Stream {
346  private:
347   friend class RelativeSeekableReader;
348   friend class NonSeekableReader;
349 
Reader(Seekability seekability)350   constexpr Reader(Seekability seekability)
351       : Stream(true, false, seekability) {}
352 
353   using Stream::Write;
354 
DoWrite(ConstByteSpan)355   Status DoWrite(ConstByteSpan) final { return Status::Unimplemented(); }
356 };
357 
358 /// A Reader that supports at least relative seeking within some range of the
359 /// current position. Seeking beyond that or from other origins may or may not
360 /// be supported. The extent to which seeking is possible is NOT exposed by this
361 /// API.
362 ///
363 /// Use in APIs when:
364 ///   * Relative seeking is required. Usage in APIs should be rare; generally
365 ///     Reader should be used instead.
366 ///
367 /// Inherit from when:
368 ///   * Implementing a Reader that can only support seeking near the current
369 ///     position.
370 ///
371 /// A buffered Reader that only supports seeking within its buffer is a good
372 /// example of a RelativeSeekableReader.
373 class RelativeSeekableReader : public Reader {
374  protected:
RelativeSeekableReader()375   constexpr RelativeSeekableReader()
376       : RelativeSeekableReader(Seekability::kRelative) {}
377 
378  private:
379   friend class SeekableReader;
380 
RelativeSeekableReader(Seekability seekability)381   constexpr RelativeSeekableReader(Seekability seekability)
382       : Reader(seekability) {}
383 };
384 
385 /// A Reader that fully supports seeking.
386 ///
387 /// Use in APIs when:
388 ///   * Absolute seeking is required. Use Reader& if seeking is not required or
389 ///     seek failures can be handled gracefully.
390 ///
391 /// Inherit from when:
392 ///   * Implementing a reader that supports absolute seeking.
393 ///
394 class SeekableReader : public RelativeSeekableReader {
395  protected:
SeekableReader()396   constexpr SeekableReader() : RelativeSeekableReader(Seekability::kAbsolute) {}
397 };
398 
399 /// A Reader that does not support seeking. The Seek() method is hidden.
400 ///
401 /// Use in APIs when:
402 ///   * Do NOT use in APIs! If seeking is not required, use Reader& instead.
403 ///
404 /// Inherit from when:
405 ///   * Implementing a Reader that does not support seeking.
406 ///
407 class NonSeekableReader : public Reader {
408  protected:
NonSeekableReader()409   constexpr NonSeekableReader() : Reader(Seekability::kNone) {}
410 
411  private:
412   using Reader::Seek;
413 
DoSeek(ptrdiff_t,Whence)414   Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); }
415 };
416 
417 /// A Stream that supports writing but not reading. The Read() method is hidden.
418 ///
419 /// Use in APIs when:
420 ///   * Must write to, but not read from, a stream.
421 ///   * May or may not need seeking. Use a SeekableWriter& if seeking is
422 ///     required.
423 ///
424 /// Inherit from when:
425 ///   * Writer cannot be extended directly. Instead, extend SeekableWriter,
426 ///     NonSeekableWriter, or (rarely) RelativeSeekableWriter, as appropriate.
427 ///
428 /// A Writer may or may not support seeking. Check seekable() or try calling
429 /// Seek() to determine if the stream is seekable.
430 class Writer : public Stream {
431  private:
432   friend class RelativeSeekableWriter;
433   friend class NonSeekableWriter;
434 
Writer(Seekability seekability)435   constexpr Writer(Seekability seekability)
436       : Stream(false, true, seekability) {}
437 
438   using Stream::Read;
439 
DoRead(ByteSpan)440   StatusWithSize DoRead(ByteSpan) final {
441     return StatusWithSize::Unimplemented();
442   }
443 };
444 
445 /// A Writer that supports at least relative seeking within some range of the
446 /// current position. Seeking beyond that or from other origins may or may not
447 /// be supported. The extent to which seeking is possible is NOT exposed by this
448 /// API.
449 ///
450 /// Use in APIs when:
451 ///   * Relative seeking is required. Usage in APIs should be rare; generally
452 ///     Writer should be used instead.
453 ///
454 /// Inherit from when:
455 ///   * Implementing a Writer that can only support seeking near the current
456 ///     position.
457 ///
458 /// A buffered Writer that only supports seeking within its buffer is a good
459 /// example of a RelativeSeekableWriter.
460 class RelativeSeekableWriter : public Writer {
461  protected:
RelativeSeekableWriter()462   constexpr RelativeSeekableWriter()
463       : RelativeSeekableWriter(Seekability::kRelative) {}
464 
465  private:
466   friend class SeekableWriter;
467 
RelativeSeekableWriter(Seekability seekability)468   constexpr RelativeSeekableWriter(Seekability seekability)
469       : Writer(seekability) {}
470 };
471 
472 /// A Writer that fully supports seeking.
473 ///
474 /// Use in APIs when:
475 ///   * Absolute seeking is required. Use Writer& if seeking is not required or
476 ///     seek failures can be handled gracefully.
477 ///
478 /// Inherit from when:
479 ///   * Implementing a writer that supports absolute seeking.
480 ///
481 class SeekableWriter : public RelativeSeekableWriter {
482  protected:
SeekableWriter()483   constexpr SeekableWriter() : RelativeSeekableWriter(Seekability::kAbsolute) {}
484 };
485 
486 /// A Writer that does not support seeking. The Seek() method is hidden.
487 ///
488 /// Use in APIs when:
489 ///   * Do NOT use in APIs! If seeking is not required, use Writer& instead.
490 ///
491 /// Inherit from when:
492 ///   * Implementing a Writer that does not support seeking.
493 ///
494 class NonSeekableWriter : public Writer {
495  protected:
NonSeekableWriter()496   constexpr NonSeekableWriter() : Writer(Seekability::kNone) {}
497 
498  private:
499   using Writer::Seek;
500 
DoSeek(ptrdiff_t,Whence)501   Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); }
502 };
503 
504 /// A Stream that supports both reading and writing.
505 ///
506 /// Use in APIs when:
507 ///   * Must both read from and write to a stream.
508 ///   * May or may not need seeking. Use a SeekableReaderWriter& if seeking is
509 ///     required.
510 ///
511 /// Inherit from when:
512 ///   * Cannot extend ReaderWriter directly. Instead, extend
513 ///     SeekableReaderWriter, NonSeekableReaderWriter, or (rarely)
514 ///     RelativeSeekableReaderWriter, as appropriate.
515 ///
516 /// A ReaderWriter may or may not support seeking. Check seekable() or try
517 /// calling Seek() to determine if the stream is seekable.
518 class ReaderWriter : public Stream {
519  public:
520   // ReaderWriters may be used as Readers.
as_reader()521   Reader& as_reader() { return internal::SiblingCast<Reader&, Stream>(*this); }
as_reader()522   const Reader& as_reader() const {
523     return internal::SiblingCast<const Reader&, Stream>(*this);
524   }
525 
526   operator Reader&() { return as_reader(); }
527   operator const Reader&() const { return as_reader(); }
528 
529   // ReaderWriters may be used as Writers.
as_writer()530   Writer& as_writer() { return internal::SiblingCast<Writer&, Stream>(*this); }
as_writer()531   const Writer& as_writer() const {
532     return internal::SiblingCast<const Writer&, Stream>(*this);
533   }
534 
535   operator Writer&() { return as_writer(); }
536   operator const Writer&() const { return as_writer(); }
537 
538  private:
539   friend class RelativeSeekableReaderWriter;
540   friend class NonSeekableReaderWriter;
541 
ReaderWriter(Seekability seekability)542   constexpr ReaderWriter(Seekability seekability)
543       : Stream(true, true, seekability) {}
544 };
545 
546 /// A ReaderWriter that supports at least relative seeking within some range of
547 /// the current position. Seeking beyond that or from other origins may or may
548 /// not be supported. The extent to which seeking is possible is NOT exposed by
549 /// this API.
550 ///
551 /// Use in APIs when:
552 ///   * Relative seeking is required. Usage in APIs should be rare; generally
553 ///     ReaderWriter should be used instead.
554 ///
555 /// Inherit from when:
556 ///   * Implementing a ReaderWriter that can only support seeking near the
557 ///     current position.
558 ///
559 /// A buffered ReaderWriter that only supports seeking within its buffer is a
560 /// good example of a RelativeSeekableReaderWriter.
561 class RelativeSeekableReaderWriter : public ReaderWriter {
562  public:
563   // RelativeSeekableReaderWriters may be used as RelativeSeekableReaders or
564   // RelativeSeekableWriters.
565   operator RelativeSeekableReader&() {
566     return internal::SiblingCast<RelativeSeekableReader&, Stream>(*this);
567   }
568   operator const RelativeSeekableReader&() const {
569     return internal::SiblingCast<const RelativeSeekableReader&, Stream>(*this);
570   }
571   operator RelativeSeekableWriter&() {
572     return internal::SiblingCast<RelativeSeekableWriter&, Stream>(*this);
573   }
574   operator const RelativeSeekableWriter&() const {
575     return internal::SiblingCast<const RelativeSeekableWriter&, Stream>(*this);
576   }
577 
578  protected:
RelativeSeekableReaderWriter()579   constexpr RelativeSeekableReaderWriter()
580       : ReaderWriter(Seekability::kRelative) {}
581 
582  private:
583   friend class SeekableReaderWriter;
584 
RelativeSeekableReaderWriter(Seekability seekability)585   constexpr RelativeSeekableReaderWriter(Seekability seekability)
586       : ReaderWriter(seekability) {}
587 };
588 
589 /// A ReaderWriter that fully supports seeking.
590 ///
591 /// Use in APIs when:
592 ///   * Absolute seeking is required. Use ReaderWriter& if seeking is not
593 ///     required or seek failures can be handled gracefully.
594 ///
595 /// Inherit from when:
596 ///   * Implementing a writer that supports absolute seeking.
597 ///
598 class SeekableReaderWriter : public RelativeSeekableReaderWriter {
599  public:
600   // SeekableReaderWriters may be used as SeekableReaders.
as_seekable_reader()601   SeekableReader& as_seekable_reader() {
602     return internal::SiblingCast<SeekableReader&, Stream>(*this);
603   }
as_seekable_reader()604   const SeekableReader& as_seekable_reader() const {
605     return internal::SiblingCast<const SeekableReader&, Stream>(*this);
606   }
607 
608   operator SeekableReader&() { return as_seekable_reader(); }
609   operator const SeekableReader&() const { return as_seekable_reader(); }
610 
611   // SeekableReaderWriters may be used as SeekableWriters.
as_seekable_writer()612   SeekableWriter& as_seekable_writer() {
613     return internal::SiblingCast<SeekableWriter&, Stream>(*this);
614   }
as_seekable_writer()615   const SeekableWriter& as_seekable_writer() const {
616     return internal::SiblingCast<const SeekableWriter&, Stream>(*this);
617   }
618 
619   operator SeekableWriter&() { return as_seekable_writer(); }
620   operator const SeekableWriter&() const { return as_seekable_writer(); }
621 
622  protected:
SeekableReaderWriter()623   constexpr SeekableReaderWriter()
624       : RelativeSeekableReaderWriter(Seekability::kAbsolute) {}
625 };
626 
627 /// A ReaderWriter that does not support seeking. The Seek() method is hidden.
628 ///
629 /// Use in APIs when:
630 ///   * Do NOT use in APIs! If seeking is not required, use ReaderWriter&
631 ///     instead.
632 ///
633 /// Inherit from when:
634 ///   * Implementing a ReaderWriter that does not support seeking.
635 ///
636 class NonSeekableReaderWriter : public ReaderWriter {
637  public:
638   // NonSeekableReaderWriters may be used as either NonSeekableReaders or
639   // NonSeekableWriters. Note that NonSeekableReaderWriter& generally should not
640   // be used in APIs, which should accept ReaderWriter& instead.
641   operator NonSeekableReader&() {
642     return internal::SiblingCast<NonSeekableReader&, Stream>(*this);
643   }
644   operator const NonSeekableReader&() const {
645     return internal::SiblingCast<const NonSeekableReader&, Stream>(*this);
646   }
647   operator NonSeekableWriter&() {
648     return internal::SiblingCast<NonSeekableWriter&, Stream>(*this);
649   }
650   operator const NonSeekableWriter&() const {
651     return internal::SiblingCast<const NonSeekableWriter&, Stream>(*this);
652   }
653 
654  protected:
NonSeekableReaderWriter()655   constexpr NonSeekableReaderWriter() : ReaderWriter(Seekability::kNone) {}
656 
657  private:
658   using ReaderWriter::Seek;
659 
DoSeek(ptrdiff_t,Whence)660   Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); }
661 };
662 
663 }  // namespace pw::stream
664