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 /// Writes data to this stream. Data is not guaranteed to be fully written out 135 /// to final resting place on Write return. 136 /// 137 /// If the writer is unable to fully accept the input data size it will abort 138 /// the write and return `RESOURCE_EXHAUSTED`. 139 /// 140 /// If the writer has been exhausted and is and can no longer accept 141 /// additional bytes it will return `OUT_OF_RANGE`. This is similar to 142 /// end-of-file (EOF). Write will only return `OUT_OF_RANGE` if 143 /// ConservativeWriteLimit() is and will remain zero. A Write operation that 144 /// is successful and also exhausts the writer returns OK, with all following 145 /// calls returning `OUT_OF_RANGE`. When ConservativeWriteLimit() is greater 146 /// than zero, a Write that is a number of bytes beyond what will exhaust the 147 /// Write will abort and return `RESOURCE_EXHAUSTED` rather than OUT_OF_RANGE 148 /// because the writer is still able to write bytes. 149 /// 150 /// Derived classes should NOT try to override the public Write methods. 151 /// Instead, provide an implementation by overriding DoWrite(). 152 /// 153 /// @returns @rst 154 /// 155 /// .. pw-status-codes:: 156 /// 157 /// OK: Data was successfully accepted by the stream. 158 /// 159 /// UNIMPLEMENTED: This stream does not support writing. 160 /// 161 /// FAILED_PRECONDITION: The writer is not in a state to accept data. 162 /// 163 /// RESOURCE_EXHAUSTED: The writer was unable to write all of requested 164 /// data at this time. No data was written. 165 /// 166 /// OUT_OF_RANGE: The Writer has been exhausted, similar to EOF. No 167 /// data was written; no more will be written. 168 /// 169 /// @endrst Write(ConstByteSpan data)170 Status Write(ConstByteSpan data) { 171 PW_DASSERT(data.empty() || data.data() != nullptr); 172 return DoWrite(data); 173 } 174 /// @overload Write(const void * data,size_t size_bytes)175 Status Write(const void* data, size_t size_bytes) { 176 return Write(span(static_cast<const std::byte*>(data), size_bytes)); 177 } 178 /// @overload Write(const std::byte b)179 Status Write(const std::byte b) { return Write(&b, 1); } 180 181 /// Changes the current position in the stream for both reading and writing, 182 /// if supported. 183 /// 184 /// Seeking to a negative offset is invalid. The behavior when seeking beyond 185 /// the end of a stream is determined by the implementation. The 186 /// implementation could fail with OUT_OF_RANGE or append bytes to the stream. 187 /// 188 /// @returns @rst 189 /// 190 /// .. pw-status-codes:: 191 /// 192 /// OK: Successfully updated the position. 193 /// 194 /// UNIMPLEMENTED: Seeking is not supported for this stream. 195 /// 196 /// OUT_OF_RANGE: Attempted to seek beyond the bounds of the 197 /// stream. The position is unchanged. 198 /// 199 /// @endrst 200 Status Seek(ptrdiff_t offset, Whence origin = kBeginning) { 201 return DoSeek(offset, origin); 202 } 203 204 /// Returns the current position in the stream, if supported. The position is 205 /// the offset from the beginning of the stream. Returns 206 /// Stream::kUnknownPosition (`size_t(-1)`) if the position is unknown. 207 /// 208 /// Streams that support seeking from the beginning always support Tell(). 209 /// Other streams may or may not support Tell(). Tell()210 size_t Tell() { return DoTell(); } 211 212 /// Liklely (not guaranteed) minimum bytes available to read at this time. 213 /// This number is advisory and not guaranteed to read full number of 214 /// requested bytes or without a `RESOURCE_EXHAUSTED` or `OUT_OF_RANGE`. As 215 /// Reader processes/handles/receives enqueued data or other contexts read 216 /// data this number can go up or down for some Readers. 217 /// 218 /// @retval zero if, in the current state, Read() would not return OkStatus(). 219 /// @retval kUnlimited if the implementation imposes no limits on read sizes. ConservativeReadLimit()220 size_t ConservativeReadLimit() const { 221 return ConservativeLimit(LimitType::kRead); 222 } 223 224 /// Likely (not guaranteed) minimum bytes available to write at this time. 225 /// This number is advisory and not guaranteed to write without a 226 /// `RESOURCE_EXHAUSTED` or `OUT_OF_RANGE`. As Writer processes/handles 227 /// enqueued of other contexts write data this number can go up or down for 228 /// some Writers. Returns zero if, in the current state, Write() would not 229 /// return OkStatus(). 230 /// 231 /// Returns kUnlimited if the implementation has no limits on write sizes. ConservativeWriteLimit()232 size_t ConservativeWriteLimit() const { 233 return ConservativeLimit(LimitType::kWrite); 234 } 235 236 protected: 237 // Used to indicate the type of limit being queried in ConservativeLimit. 238 enum class LimitType : bool { kRead, kWrite }; 239 240 private: 241 // The Stream class should not be extended directly, so its constructor is 242 // private. To implement a new Stream, extend one of its derived classes. 243 friend class Reader; 244 friend class RelativeSeekableReader; 245 friend class SeekableReader; 246 friend class NonSeekableReader; 247 248 friend class Writer; 249 friend class RelativeSeekableWriter; 250 friend class SeekableWriter; 251 friend class NonSeekableWriter; 252 253 friend class ReaderWriter; 254 friend class RelativeSeekableReaderWriter; 255 friend class SeekableReaderWriter; 256 friend class NonSeekableReaderWriter; 257 258 /// Seekability expresses the origins from which the stream always supports 259 /// seeking. Seeking from other origins may work, but is not guaranteed. 260 /// 261 /// Seekability is implemented as a bitfield of Whence values. 262 enum class Seekability : uint8_t { 263 /// No type of seeking is supported. 264 kNone = 0, 265 266 /// Seeking from the current position is supported, but the range may be 267 /// limited. For example, a buffered stream might support seeking within the 268 /// buffered data, but not before or after. 269 kRelative = kCurrent, 270 271 /// The stream supports random access anywhere within the stream. 272 kAbsolute = kBeginning | kCurrent | kEnd, 273 }; 274 275 // These are the virtual methods that are implemented by derived classes. The 276 // public API functions call these virtual functions. 277 Stream(bool readable,bool writable,Seekability seekability)278 constexpr Stream(bool readable, bool writable, Seekability seekability) 279 : readable_(readable), writable_(writable), seekability_(seekability) {} 280 281 /// Virtual Read() function implemented by derived classes. 282 virtual StatusWithSize DoRead(ByteSpan destination) = 0; 283 284 /// Virtual Write() function implemented by derived classes. 285 virtual Status DoWrite(ConstByteSpan data) = 0; 286 287 /// Virtual Seek() function implemented by derived classes. 288 virtual Status DoSeek(ptrdiff_t offset, Whence origin) = 0; 289 290 /// Virtual Tell() function optionally implemented by derived classes. 291 /// The default implementation always returns kUnknownPosition. DoTell()292 virtual size_t DoTell() { return kUnknownPosition; } 293 294 /// Virtual function optionally implemented by derived classes that is used 295 /// for ConservativeReadLimit() and ConservativeWriteLimit(). 296 /// 297 /// The default implementation returns kUnlimited or ``0`` depending on 298 /// whether the stream is readable/writable. ConservativeLimit(LimitType limit_type)299 virtual size_t ConservativeLimit(LimitType limit_type) const { 300 if (limit_type == LimitType::kRead) { 301 return readable() ? kUnlimited : 0; 302 } 303 return writable() ? kUnlimited : 0; 304 } 305 306 bool readable_; 307 bool writable_; 308 Seekability seekability_; 309 }; 310 311 /// A Stream that supports reading but not writing. The Write() method is 312 /// hidden. 313 /// 314 /// Use in APIs when: 315 /// * Must read from, but not write to, a stream. 316 /// * May or may not need seeking. Use a SeekableReader& if seeking is 317 /// required. 318 /// 319 /// Inherit from when: 320 /// * Reader cannot be extended directly. Instead, extend SeekableReader, 321 /// NonSeekableReader, or (rarely) RelativeSeekableReader, as appropriate. 322 /// 323 /// A Reader may or may not support seeking. Check seekable() or try calling 324 /// Seek() to determine if the stream is seekable. 325 class Reader : public Stream { 326 private: 327 friend class RelativeSeekableReader; 328 friend class NonSeekableReader; 329 Reader(Seekability seekability)330 constexpr Reader(Seekability seekability) 331 : Stream(true, false, seekability) {} 332 333 using Stream::Write; 334 DoWrite(ConstByteSpan)335 Status DoWrite(ConstByteSpan) final { return Status::Unimplemented(); } 336 }; 337 338 /// A Reader that supports at least relative seeking within some range of the 339 /// current position. Seeking beyond that or from other origins may or may not 340 /// be supported. The extent to which seeking is possible is NOT exposed by this 341 /// API. 342 /// 343 /// Use in APIs when: 344 /// * Relative seeking is required. Usage in APIs should be rare; generally 345 /// Reader should be used instead. 346 /// 347 /// Inherit from when: 348 /// * Implementing a Reader that can only support seeking near the current 349 /// position. 350 /// 351 /// A buffered Reader that only supports seeking within its buffer is a good 352 /// example of a RelativeSeekableReader. 353 class RelativeSeekableReader : public Reader { 354 protected: RelativeSeekableReader()355 constexpr RelativeSeekableReader() 356 : RelativeSeekableReader(Seekability::kRelative) {} 357 358 private: 359 friend class SeekableReader; 360 RelativeSeekableReader(Seekability seekability)361 constexpr RelativeSeekableReader(Seekability seekability) 362 : Reader(seekability) {} 363 }; 364 365 /// A Reader that fully supports seeking. 366 /// 367 /// Use in APIs when: 368 /// * Absolute seeking is required. Use Reader& if seeking is not required or 369 /// seek failures can be handled gracefully. 370 /// 371 /// Inherit from when: 372 /// * Implementing a reader that supports absolute seeking. 373 /// 374 class SeekableReader : public RelativeSeekableReader { 375 protected: SeekableReader()376 constexpr SeekableReader() : RelativeSeekableReader(Seekability::kAbsolute) {} 377 }; 378 379 /// A Reader that does not support seeking. The Seek() method is hidden. 380 /// 381 /// Use in APIs when: 382 /// * Do NOT use in APIs! If seeking is not required, use Reader& instead. 383 /// 384 /// Inherit from when: 385 /// * Implementing a Reader that does not support seeking. 386 /// 387 class NonSeekableReader : public Reader { 388 protected: NonSeekableReader()389 constexpr NonSeekableReader() : Reader(Seekability::kNone) {} 390 391 private: 392 using Reader::Seek; 393 DoSeek(ptrdiff_t,Whence)394 Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); } 395 }; 396 397 /// A Stream that supports writing but not reading. The Read() method is hidden. 398 /// 399 /// Use in APIs when: 400 /// * Must write to, but not read from, a stream. 401 /// * May or may not need seeking. Use a SeekableWriter& if seeking is 402 /// required. 403 /// 404 /// Inherit from when: 405 /// * Writer cannot be extended directly. Instead, extend SeekableWriter, 406 /// NonSeekableWriter, or (rarely) RelativeSeekableWriter, as appropriate. 407 /// 408 /// A Writer may or may not support seeking. Check seekable() or try calling 409 /// Seek() to determine if the stream is seekable. 410 class Writer : public Stream { 411 private: 412 friend class RelativeSeekableWriter; 413 friend class NonSeekableWriter; 414 Writer(Seekability seekability)415 constexpr Writer(Seekability seekability) 416 : Stream(false, true, seekability) {} 417 418 using Stream::Read; 419 DoRead(ByteSpan)420 StatusWithSize DoRead(ByteSpan) final { 421 return StatusWithSize::Unimplemented(); 422 } 423 }; 424 425 /// A Writer that supports at least relative seeking within some range of the 426 /// current position. Seeking beyond that or from other origins may or may not 427 /// be supported. The extent to which seeking is possible is NOT exposed by this 428 /// API. 429 /// 430 /// Use in APIs when: 431 /// * Relative seeking is required. Usage in APIs should be rare; generally 432 /// Writer should be used instead. 433 /// 434 /// Inherit from when: 435 /// * Implementing a Writer that can only support seeking near the current 436 /// position. 437 /// 438 /// A buffered Writer that only supports seeking within its buffer is a good 439 /// example of a RelativeSeekableWriter. 440 class RelativeSeekableWriter : public Writer { 441 protected: RelativeSeekableWriter()442 constexpr RelativeSeekableWriter() 443 : RelativeSeekableWriter(Seekability::kRelative) {} 444 445 private: 446 friend class SeekableWriter; 447 RelativeSeekableWriter(Seekability seekability)448 constexpr RelativeSeekableWriter(Seekability seekability) 449 : Writer(seekability) {} 450 }; 451 452 /// A Writer that fully supports seeking. 453 /// 454 /// Use in APIs when: 455 /// * Absolute seeking is required. Use Writer& if seeking is not required or 456 /// seek failures can be handled gracefully. 457 /// 458 /// Inherit from when: 459 /// * Implementing a writer that supports absolute seeking. 460 /// 461 class SeekableWriter : public RelativeSeekableWriter { 462 protected: SeekableWriter()463 constexpr SeekableWriter() : RelativeSeekableWriter(Seekability::kAbsolute) {} 464 }; 465 466 /// A Writer that does not support seeking. The Seek() method is hidden. 467 /// 468 /// Use in APIs when: 469 /// * Do NOT use in APIs! If seeking is not required, use Writer& instead. 470 /// 471 /// Inherit from when: 472 /// * Implementing a Writer that does not support seeking. 473 /// 474 class NonSeekableWriter : public Writer { 475 protected: NonSeekableWriter()476 constexpr NonSeekableWriter() : Writer(Seekability::kNone) {} 477 478 private: 479 using Writer::Seek; 480 DoSeek(ptrdiff_t,Whence)481 Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); } 482 }; 483 484 /// A Stream that supports both reading and writing. 485 /// 486 /// Use in APIs when: 487 /// * Must both read from and write to a stream. 488 /// * May or may not need seeking. Use a SeekableReaderWriter& if seeking is 489 /// required. 490 /// 491 /// Inherit from when: 492 /// * Cannot extend ReaderWriter directly. Instead, extend 493 /// SeekableReaderWriter, NonSeekableReaderWriter, or (rarely) 494 /// RelativeSeekableReaderWriter, as appropriate. 495 /// 496 /// A ReaderWriter may or may not support seeking. Check seekable() or try 497 /// calling Seek() to determine if the stream is seekable. 498 class ReaderWriter : public Stream { 499 public: 500 // ReaderWriters may be used as Readers. as_reader()501 Reader& as_reader() { return internal::SiblingCast<Reader&, Stream>(*this); } as_reader()502 const Reader& as_reader() const { 503 return internal::SiblingCast<const Reader&, Stream>(*this); 504 } 505 506 operator Reader&() { return as_reader(); } 507 operator const Reader&() const { return as_reader(); } 508 509 // ReaderWriters may be used as Writers. as_writer()510 Writer& as_writer() { return internal::SiblingCast<Writer&, Stream>(*this); } as_writer()511 const Writer& as_writer() const { 512 return internal::SiblingCast<const Writer&, Stream>(*this); 513 } 514 515 operator Writer&() { return as_writer(); } 516 operator const Writer&() const { return as_writer(); } 517 518 private: 519 friend class RelativeSeekableReaderWriter; 520 friend class NonSeekableReaderWriter; 521 ReaderWriter(Seekability seekability)522 constexpr ReaderWriter(Seekability seekability) 523 : Stream(true, true, seekability) {} 524 }; 525 526 /// A ReaderWriter that supports at least relative seeking within some range of 527 /// the current position. Seeking beyond that or from other origins may or may 528 /// not be supported. The extent to which seeking is possible is NOT exposed by 529 /// this API. 530 /// 531 /// Use in APIs when: 532 /// * Relative seeking is required. Usage in APIs should be rare; generally 533 /// ReaderWriter should be used instead. 534 /// 535 /// Inherit from when: 536 /// * Implementing a ReaderWriter that can only support seeking near the 537 /// current position. 538 /// 539 /// A buffered ReaderWriter that only supports seeking within its buffer is a 540 /// good example of a RelativeSeekableReaderWriter. 541 class RelativeSeekableReaderWriter : public ReaderWriter { 542 public: 543 // RelativeSeekableReaderWriters may be used as RelativeSeekableReaders or 544 // RelativeSeekableWriters. 545 operator RelativeSeekableReader&() { 546 return internal::SiblingCast<RelativeSeekableReader&, Stream>(*this); 547 } 548 operator const RelativeSeekableReader&() const { 549 return internal::SiblingCast<const RelativeSeekableReader&, Stream>(*this); 550 } 551 operator RelativeSeekableWriter&() { 552 return internal::SiblingCast<RelativeSeekableWriter&, Stream>(*this); 553 } 554 operator const RelativeSeekableWriter&() const { 555 return internal::SiblingCast<const RelativeSeekableWriter&, Stream>(*this); 556 } 557 558 protected: RelativeSeekableReaderWriter()559 constexpr RelativeSeekableReaderWriter() 560 : ReaderWriter(Seekability::kRelative) {} 561 562 private: 563 friend class SeekableReaderWriter; 564 RelativeSeekableReaderWriter(Seekability seekability)565 constexpr RelativeSeekableReaderWriter(Seekability seekability) 566 : ReaderWriter(seekability) {} 567 }; 568 569 /// A ReaderWriter that fully supports seeking. 570 /// 571 /// Use in APIs when: 572 /// * Absolute seeking is required. Use ReaderWriter& if seeking is not 573 /// required or seek failures can be handled gracefully. 574 /// 575 /// Inherit from when: 576 /// * Implementing a writer that supports absolute seeking. 577 /// 578 class SeekableReaderWriter : public RelativeSeekableReaderWriter { 579 public: 580 // SeekableReaderWriters may be used as SeekableReaders. as_seekable_reader()581 SeekableReader& as_seekable_reader() { 582 return internal::SiblingCast<SeekableReader&, Stream>(*this); 583 } as_seekable_reader()584 const SeekableReader& as_seekable_reader() const { 585 return internal::SiblingCast<const SeekableReader&, Stream>(*this); 586 } 587 588 operator SeekableReader&() { return as_seekable_reader(); } 589 operator const SeekableReader&() const { return as_seekable_reader(); } 590 591 // SeekableReaderWriters may be used as SeekableWriters. as_seekable_writer()592 SeekableWriter& as_seekable_writer() { 593 return internal::SiblingCast<SeekableWriter&, Stream>(*this); 594 } as_seekable_writer()595 const SeekableWriter& as_seekable_writer() const { 596 return internal::SiblingCast<const SeekableWriter&, Stream>(*this); 597 } 598 599 operator SeekableWriter&() { return as_seekable_writer(); } 600 operator const SeekableWriter&() const { return as_seekable_writer(); } 601 602 protected: SeekableReaderWriter()603 constexpr SeekableReaderWriter() 604 : RelativeSeekableReaderWriter(Seekability::kAbsolute) {} 605 }; 606 607 /// A ReaderWriter that does not support seeking. The Seek() method is hidden. 608 /// 609 /// Use in APIs when: 610 /// * Do NOT use in APIs! If seeking is not required, use ReaderWriter& 611 /// instead. 612 /// 613 /// Inherit from when: 614 /// * Implementing a ReaderWriter that does not support seeking. 615 /// 616 class NonSeekableReaderWriter : public ReaderWriter { 617 public: 618 // NonSeekableReaderWriters may be used as either NonSeekableReaders or 619 // NonSeekableWriters. Note that NonSeekableReaderWriter& generally should not 620 // be used in APIs, which should accept ReaderWriter& instead. 621 operator NonSeekableReader&() { 622 return internal::SiblingCast<NonSeekableReader&, Stream>(*this); 623 } 624 operator const NonSeekableReader&() const { 625 return internal::SiblingCast<const NonSeekableReader&, Stream>(*this); 626 } 627 operator NonSeekableWriter&() { 628 return internal::SiblingCast<NonSeekableWriter&, Stream>(*this); 629 } 630 operator const NonSeekableWriter&() const { 631 return internal::SiblingCast<const NonSeekableWriter&, Stream>(*this); 632 } 633 634 protected: NonSeekableReaderWriter()635 constexpr NonSeekableReaderWriter() : ReaderWriter(Seekability::kNone) {} 636 637 private: 638 using ReaderWriter::Seek; 639 DoSeek(ptrdiff_t,Whence)640 Status DoSeek(ptrdiff_t, Whence) final { return Status::Unimplemented(); } 641 }; 642 643 } // namespace pw::stream 644