text: // Copyright 2020 The Pigweed Authors
  //
  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  // use this file except in compliance with the License. You may obtain a copy of
  // the License at
  //
  //     https://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  // License for the specific language governing permissions and limitations under
  // the License.
  
  #include "pw_stream/interval_reader.h"
  
  #include "pw_assert/check.h"
  
  namespace pw::stream {
  
  void IntervalReader::Check() {
    PW_CHECK(ok(), "internal::IntervalReader is in an invalid state");
    PW_CHECK_NOTNULL(source_reader_);
  }
  
  StatusWithSize IntervalReader::DoRead(ByteSpan destination) {
    Check();
    if (current_ == end_) {
      return StatusWithSize::OutOfRange();
    }
  
    // Seek the source reader to the `current_` offset of this IntervalReader
    // before reading.
    Status status = source_reader_->Seek(current_, Whence::kBeginning);
    if (!status.ok()) {
      return StatusWithSize(status, 0);
    }
  
    size_t to_read = std::min(destination.size(), end_ - current_);
    Result<ByteSpan> res = source_reader_->Read(destination.first(to_read));
    if (!res.ok()) {
      return StatusWithSize(res.status(), 0);
    }
  
    current_ += res.value().size();
    return StatusWithSize(res.value().size());
  }
  
  Status IntervalReader::DoSeek(ssize_t offset, Whence origin) {
    Check();
  
    ssize_t absolute_position = std::numeric_limits<size_t>::max();
  
    // Convert from the position within the interval to the position within the
    // source reader stream.
    switch (origin) {
      case Whence::kBeginning:
        absolute_position = offset + start_;
        break;
  
      case Whence::kCurrent:
        absolute_position = current_ + offset;
        break;
  
      case Whence::kEnd:
        absolute_position = end_ + offset;
        break;
    }
  
    if (absolute_position < 0) {
      return Status::InvalidArgument();
    }
  
    if (static_cast<size_t>(absolute_position) < start_ ||
        static_cast<size_t>(absolute_position) > end_) {
      return Status::InvalidArgument();
    }
  
    current_ = absolute_position;
    return OkStatus();
  }
  
  }  // namespace pw::stream
  