| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * 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 |
| * |
| * http://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 "types.h" |
| |
| IOVector& IOVector::operator=(IOVector&& move) noexcept { |
| chain_ = std::move(move.chain_); |
| chain_length_ = move.chain_length_; |
| begin_offset_ = move.begin_offset_; |
| start_index_ = move.start_index_; |
| |
| move.clear(); |
| return *this; |
| } |
| |
| IOVector::block_type IOVector::clear() { |
| chain_length_ = 0; |
| begin_offset_ = 0; |
| start_index_ = 0; |
| block_type res; |
| if (!chain_.empty()) { |
| res = std::move(chain_.back()); |
| } |
| chain_.clear(); |
| return res; |
| } |
| |
| void IOVector::drop_front(IOVector::size_type len) { |
| if (len == 0) { |
| return; |
| } |
| if (len == size()) { |
| clear(); |
| return; |
| } |
| CHECK_LT(len, size()); |
| |
| auto dropped = 0u; |
| while (dropped < len) { |
| const auto next = chain_[start_index_].size() - begin_offset_; |
| if (dropped + next < len) { |
| pop_front_block(); |
| dropped += next; |
| } else { |
| const auto taken = len - dropped; |
| begin_offset_ += taken; |
| break; |
| } |
| } |
| } |
| |
| IOVector IOVector::take_front(IOVector::size_type len) { |
| if (len == 0) { |
| return {}; |
| } |
| if (len == size()) { |
| return std::move(*this); |
| } |
| |
| CHECK_GE(size(), len); |
| IOVector res; |
| // first iterate over the blocks that completely go into the other vector |
| while (chain_[start_index_].size() - begin_offset_ <= len) { |
| chain_length_ -= chain_[start_index_].size(); |
| len -= chain_[start_index_].size() - begin_offset_; |
| if (chain_[start_index_].size() > begin_offset_) { |
| res.append(std::move(chain_[start_index_])); |
| if (begin_offset_) { |
| res.begin_offset_ = std::exchange(begin_offset_, 0); |
| } |
| } else { |
| begin_offset_ = 0; |
| } |
| ++start_index_; |
| } |
| |
| if (len > 0) { |
| // what's left is a single buffer that needs to be split between the |res| and |this| |
| // we know that it has to be split - there was a check for the case when it has to |
| // go away as a whole. |
| if (begin_offset_ != 0 || len < chain_[start_index_].size() / 2) { |
| // let's memcpy the data out |
| block_type block(chain_[start_index_].begin() + begin_offset_, |
| chain_[start_index_].begin() + begin_offset_ + len); |
| res.append(std::move(block)); |
| begin_offset_ += len; |
| } else { |
| CHECK_EQ(begin_offset_, 0u); |
| // move out the internal buffer out and copy only the tail of it back in |
| block_type block(chain_[start_index_].begin() + len, chain_[start_index_].end()); |
| chain_length_ -= chain_[start_index_].size(); |
| chain_[start_index_].resize(len); |
| res.append(std::move(chain_[start_index_])); |
| chain_length_ += block.size(); |
| chain_[start_index_] = std::move(block); |
| } |
| } |
| return res; |
| } |
| |
| void IOVector::trim_front() { |
| if ((begin_offset_ == 0 && start_index_ == 0) || chain_.empty()) { |
| return; |
| } |
| block_type& first_block = chain_[start_index_]; |
| if (begin_offset_ == first_block.size()) { |
| ++start_index_; |
| } else { |
| memmove(first_block.data(), first_block.data() + begin_offset_, |
| first_block.size() - begin_offset_); |
| first_block.resize(first_block.size() - begin_offset_); |
| } |
| chain_length_ -= begin_offset_; |
| begin_offset_ = 0; |
| trim_chain_front(); |
| } |
| |
| void IOVector::trim_chain_front() { |
| if (start_index_) { |
| chain_.erase(chain_.begin(), chain_.begin() + start_index_); |
| start_index_ = 0; |
| } |
| } |
| |
| void IOVector::pop_front_block() { |
| chain_length_ -= chain_[start_index_].size(); |
| begin_offset_ = 0; |
| chain_[start_index_].clear(); |
| ++start_index_; |
| if (start_index_ > std::max<size_t>(4, chain_.size() / 2)) { |
| trim_chain_front(); |
| } |
| } |
| |
| IOVector::block_type IOVector::coalesce() && { |
| // Destructive coalesce() may optimize for several cases when it doesn't need to allocate |
| // new buffer, or even return one of the existing blocks as is. The only guarantee is that |
| // after this call the IOVector is in some valid state. Nothing is guaranteed about the |
| // specifics. |
| if (size() == 0) { |
| return {}; |
| } |
| if (begin_offset_ == chain_[start_index_].size() && chain_.size() == start_index_ + 2) { |
| chain_length_ -= chain_.back().size(); |
| auto res = std::move(chain_.back()); |
| chain_.pop_back(); |
| return res; |
| } |
| if (chain_.size() == start_index_ + 1) { |
| chain_length_ -= chain_.back().size(); |
| auto res = std::move(chain_.back()); |
| chain_.pop_back(); |
| if (begin_offset_ != 0) { |
| memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_); |
| res.resize(res.size() - begin_offset_); |
| begin_offset_ = 0; |
| } |
| return res; |
| } |
| if (auto& firstBuffer = chain_[start_index_]; firstBuffer.capacity() >= size()) { |
| auto res = std::move(chain_[start_index_]); |
| auto size = res.size(); |
| chain_length_ -= size; |
| if (begin_offset_ != 0) { |
| memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_); |
| size -= begin_offset_; |
| begin_offset_ = 0; |
| } |
| for (auto i = start_index_ + 1; i < chain_.size(); ++i) { |
| memcpy(res.data() + size, chain_[i].data(), chain_[i].size()); |
| size += chain_[i].size(); |
| } |
| res.resize(size); |
| ++start_index_; |
| return res; |
| } |
| return const_cast<const IOVector*>(this)->coalesce<>(); |
| } |
| |
| std::vector<adb_iovec> IOVector::iovecs() const { |
| std::vector<adb_iovec> result; |
| result.reserve(chain_.size() - start_index_); |
| iterate_blocks([&result](const char* data, size_t len) { |
| adb_iovec iov; |
| iov.iov_base = const_cast<char*>(data); |
| iov.iov_len = len; |
| result.emplace_back(iov); |
| }); |
| |
| return result; |
| } |