/* * 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(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(this)->coalesce<>(); } std::vector IOVector::iovecs() const { std::vector result; result.reserve(chain_.size() - start_index_); iterate_blocks([&result](const char* data, size_t len) { adb_iovec iov; iov.iov_base = const_cast(data); iov.iov_len = len; result.emplace_back(iov); }); return result; }