1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "types.h"
18
operator =(IOVector && move)19 IOVector& IOVector::operator=(IOVector&& move) noexcept {
20 chain_ = std::move(move.chain_);
21 chain_length_ = move.chain_length_;
22 begin_offset_ = move.begin_offset_;
23 start_index_ = move.start_index_;
24
25 move.clear();
26 return *this;
27 }
28
clear()29 IOVector::block_type IOVector::clear() {
30 chain_length_ = 0;
31 begin_offset_ = 0;
32 start_index_ = 0;
33 block_type res;
34 if (!chain_.empty()) {
35 res = std::move(chain_.back());
36 }
37 chain_.clear();
38 return res;
39 }
40
drop_front(IOVector::size_type len)41 void IOVector::drop_front(IOVector::size_type len) {
42 if (len == 0) {
43 return;
44 }
45 if (len == size()) {
46 clear();
47 return;
48 }
49 CHECK_LT(len, size());
50
51 auto dropped = 0u;
52 while (dropped < len) {
53 const auto next = chain_[start_index_].size() - begin_offset_;
54 if (dropped + next <= len) {
55 pop_front_block();
56 dropped += next;
57 } else {
58 const auto taken = len - dropped;
59 begin_offset_ += taken;
60 break;
61 }
62 }
63 }
64
take_front(IOVector::size_type len)65 IOVector IOVector::take_front(IOVector::size_type len) {
66 if (len == 0) {
67 return {};
68 }
69 if (len == size()) {
70 return std::move(*this);
71 }
72
73 CHECK_GE(size(), len);
74 IOVector res;
75 // first iterate over the blocks that completely go into the other vector
76 while (chain_[start_index_].size() - begin_offset_ <= len) {
77 chain_length_ -= chain_[start_index_].size();
78 len -= chain_[start_index_].size() - begin_offset_;
79 if (chain_[start_index_].size() > begin_offset_) {
80 res.append(std::move(chain_[start_index_]));
81 if (begin_offset_) {
82 res.begin_offset_ = std::exchange(begin_offset_, 0);
83 }
84 } else {
85 begin_offset_ = 0;
86 }
87 ++start_index_;
88 }
89
90 if (len > 0) {
91 // what's left is a single buffer that needs to be split between the |res| and |this|
92 // we know that it has to be split - there was a check for the case when it has to
93 // go away as a whole.
94 if (begin_offset_ != 0 || len < chain_[start_index_].size() / 2) {
95 // let's memcpy the data out
96 block_type block(chain_[start_index_].begin() + begin_offset_,
97 chain_[start_index_].begin() + begin_offset_ + len);
98 res.append(std::move(block));
99 begin_offset_ += len;
100 } else {
101 CHECK_EQ(begin_offset_, 0u);
102 // move out the internal buffer out and copy only the tail of it back in
103 block_type block(chain_[start_index_].begin() + len, chain_[start_index_].end());
104 chain_length_ -= chain_[start_index_].size();
105 chain_[start_index_].resize(len);
106 res.append(std::move(chain_[start_index_]));
107 chain_length_ += block.size();
108 chain_[start_index_] = std::move(block);
109 }
110 }
111 return res;
112 }
113
trim_front()114 void IOVector::trim_front() {
115 if ((begin_offset_ == 0 && start_index_ == 0) || chain_.empty()) {
116 return;
117 }
118 block_type& first_block = chain_[start_index_];
119 if (begin_offset_ == first_block.size()) {
120 ++start_index_;
121 } else {
122 memmove(first_block.data(), first_block.data() + begin_offset_,
123 first_block.size() - begin_offset_);
124 first_block.resize(first_block.size() - begin_offset_);
125 }
126 chain_length_ -= begin_offset_;
127 begin_offset_ = 0;
128 trim_chain_front();
129 }
130
trim_chain_front()131 void IOVector::trim_chain_front() {
132 if (start_index_) {
133 chain_.erase(chain_.begin(), chain_.begin() + start_index_);
134 start_index_ = 0;
135 }
136 }
137
pop_front_block()138 void IOVector::pop_front_block() {
139 chain_length_ -= chain_[start_index_].size();
140 begin_offset_ = 0;
141 chain_[start_index_].clear();
142 ++start_index_;
143 if (start_index_ > std::max<size_t>(4, chain_.size() / 2)) {
144 trim_chain_front();
145 }
146 }
147
coalesce()148 IOVector::block_type IOVector::coalesce() && {
149 // Destructive coalesce() may optimize for several cases when it doesn't need to allocate
150 // new buffer, or even return one of the existing blocks as is. The only guarantee is that
151 // after this call the IOVector is in some valid state. Nothing is guaranteed about the
152 // specifics.
153 if (size() == 0) {
154 return {};
155 }
156 if (begin_offset_ == chain_[start_index_].size() && chain_.size() == start_index_ + 2) {
157 chain_length_ -= chain_.back().size();
158 auto res = std::move(chain_.back());
159 chain_.pop_back();
160 return res;
161 }
162 if (chain_.size() == start_index_ + 1) {
163 chain_length_ -= chain_.back().size();
164 auto res = std::move(chain_.back());
165 chain_.pop_back();
166 if (begin_offset_ != 0) {
167 memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
168 res.resize(res.size() - begin_offset_);
169 begin_offset_ = 0;
170 }
171 return res;
172 }
173 if (auto& firstBuffer = chain_[start_index_]; firstBuffer.capacity() >= size()) {
174 auto res = std::move(chain_[start_index_]);
175 auto size = res.size();
176 chain_length_ -= size;
177 if (begin_offset_ != 0) {
178 memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
179 size -= begin_offset_;
180 begin_offset_ = 0;
181 }
182 for (auto i = start_index_ + 1; i < chain_.size(); ++i) {
183 memcpy(res.data() + size, chain_[i].data(), chain_[i].size());
184 size += chain_[i].size();
185 }
186 res.resize(size);
187 ++start_index_;
188 return res;
189 }
190 return const_cast<const IOVector*>(this)->coalesce<>();
191 }
192
iovecs() const193 std::vector<adb_iovec> IOVector::iovecs() const {
194 std::vector<adb_iovec> result;
195 result.reserve(chain_.size() - start_index_);
196 iterate_blocks([&result](const char* data, size_t len) {
197 adb_iovec iov;
198 iov.iov_base = const_cast<char*>(data);
199 iov.iov_len = len;
200 result.emplace_back(iov);
201 });
202
203 return result;
204 }
205