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