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