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