// Copyright 2023 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/multibuf.h" #include "pw_assert/check.h" namespace pw::multibuf { void MultiBuf::Release() noexcept { while (first_ != nullptr) { Chunk* removed = first_; first_ = first_->next_in_buf_; removed->Free(); } } size_t MultiBuf::size() const { size_t len = 0; for (const auto& chunk : Chunks()) { len += chunk.size(); } return len; } void MultiBuf::PushFrontChunk(OwnedChunk chunk) { PW_DCHECK(chunk->next_in_buf_ == nullptr); Chunk* new_chunk = std::move(chunk).Take(); Chunk* old_first = first_; new_chunk->next_in_buf_ = old_first; first_ = new_chunk; } MultiBuf::ChunkIterator MultiBuf::InsertChunk(ChunkIterator position, OwnedChunk chunk) { // Note: this also catches the cases where ``first_ == nullptr`` PW_DCHECK(chunk->next_in_buf_ == nullptr); if (position == ChunkBegin()) { PushFrontChunk(std::move(chunk)); return ChunkIterator(first_); } Chunk* previous = Previous(position.chunk_); Chunk* old_next = previous->next_in_buf_; Chunk* new_chunk = std::move(chunk).Take(); new_chunk->next_in_buf_ = old_next; previous->next_in_buf_ = new_chunk; return ChunkIterator(new_chunk); } std::tuple MultiBuf::TakeChunk( ChunkIterator position) { Chunk* chunk = position.chunk_; if (position == ChunkBegin()) { Chunk* old_first = first_; first_ = old_first->next_in_buf_; old_first->next_in_buf_ = nullptr; return std::make_tuple(ChunkIterator(first_), OwnedChunk(old_first)); } Chunk* previous = Previous(chunk); previous->next_in_buf_ = chunk->next_in_buf_; chunk->next_in_buf_ = nullptr; return std::make_tuple(ChunkIterator(previous->next_in_buf_), OwnedChunk(chunk)); } Chunk* MultiBuf::Previous(Chunk* chunk) const { Chunk* previous = first_; while (previous != nullptr && previous->next_in_buf_ != chunk) { previous = previous->next_in_buf_; } return previous; } MultiBuf::iterator& MultiBuf::iterator::operator++() { if (byte_index_ + 1 == chunk_->size()) { chunk_ = chunk_->next_in_buf_; byte_index_ = 0; AdvanceToData(); } else { ++byte_index_; } return *this; } void MultiBuf::iterator::AdvanceToData() { while (chunk_ != nullptr && chunk_->size() == 0) { chunk_ = chunk_->next_in_buf_; } } MultiBuf::const_iterator& MultiBuf::const_iterator::operator++() { if (byte_index_ + 1 == chunk_->size()) { chunk_ = chunk_->next_in_buf_; byte_index_ = 0; AdvanceToData(); } else { ++byte_index_; } return *this; } void MultiBuf::const_iterator::AdvanceToData() { while (chunk_ != nullptr && chunk_->size() == 0) { chunk_ = chunk_->next_in_buf_; } } size_t MultiBuf::ChunkIterable::size() const { Chunk* current = first_; size_t i = 0; while (current != nullptr) { ++i; current = current->next_in_buf_; } return i; } } // namespace pw::multibuf