blob: 26b77ab62080b2d561911376391f5500f89e3a5e [file] [log] [blame]
/*
* 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;
}