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