| // Copyright 2024 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_multibuf/stream.h" |
| |
| #include <algorithm> |
| #include <cstring> |
| |
| #include "pw_status/status.h" |
| #include "pw_status/status_with_size.h" |
| #include "pw_status/try.h" |
| #include "pw_stream/seek.h" |
| |
| namespace pw::multibuf { |
| |
| Status Stream::DoWrite(ConstByteSpan data) { |
| while (!data.empty()) { |
| if (iterator_ == multibuf_.end()) { |
| return Status::OutOfRange(); |
| } |
| |
| Chunk* current_chunk = iterator_.chunk(); |
| const size_t remaining_in_chunk = |
| current_chunk->size() - iterator_.byte_index(); |
| |
| const size_t bytes_to_copy = std::min(data.size(), remaining_in_chunk); |
| |
| std::memcpy(current_chunk->data() + iterator_.byte_index(), |
| data.data(), |
| bytes_to_copy); |
| |
| data = data.subspan(bytes_to_copy); |
| multibuf_offset_ += bytes_to_copy; |
| iterator_ += bytes_to_copy; |
| } |
| |
| return OkStatus(); |
| } |
| |
| StatusWithSize Stream::DoRead(ByteSpan destination) { |
| size_t bytes_written = 0; |
| |
| while (!destination.empty()) { |
| if (iterator_ == multibuf_.end()) { |
| if (bytes_written > 0) { |
| return StatusWithSize(bytes_written); |
| } |
| return StatusWithSize::OutOfRange(); |
| } |
| |
| Chunk* current_chunk = iterator_.chunk(); |
| const size_t remaining_in_chunk = |
| current_chunk->size() - iterator_.byte_index(); |
| |
| const size_t bytes_to_copy = |
| std::min(remaining_in_chunk, destination.size()); |
| std::memcpy(destination.data(), |
| current_chunk->data() + iterator_.byte_index(), |
| bytes_to_copy); |
| |
| destination = destination.subspan(bytes_to_copy); |
| bytes_written += bytes_to_copy; |
| multibuf_offset_ += bytes_to_copy; |
| iterator_ += bytes_to_copy; |
| } |
| |
| return StatusWithSize(bytes_written); |
| } |
| |
| Status Stream::DoSeek(ptrdiff_t offset, Whence origin) { |
| size_t new_offset = multibuf_offset_; |
| PW_TRY(stream::CalculateSeek(offset, origin, multibuf_.size(), new_offset)); |
| |
| if (new_offset < multibuf_offset_) { |
| return Status::OutOfRange(); |
| } |
| |
| iterator_ += new_offset - multibuf_offset_; |
| multibuf_offset_ = new_offset; |
| return iterator_ == multibuf_.end() ? Status::OutOfRange() : OkStatus(); |
| } |
| |
| } // namespace pw::multibuf |