• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 #pragma once
18 
19 #include <algorithm>
20 #include <deque>
21 #include <memory>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
25 
26 #include <android-base/logging.h>
27 
28 #include "sysdeps/uio.h"
29 
30 // Essentially std::vector<char>, except without zero initialization or reallocation.
31 struct Block {
32     using iterator = char*;
33 
BlockBlock34     Block() {}
35 
BlockBlock36     explicit Block(size_t size) { allocate(size); }
37 
38     template <typename Iterator>
BlockBlock39     Block(Iterator begin, Iterator end) : Block(end - begin) {
40         std::copy(begin, end, data_.get());
41     }
42 
43     Block(const Block& copy) = delete;
BlockBlock44     Block(Block&& move) noexcept {
45         std::swap(data_, move.data_);
46         std::swap(capacity_, move.capacity_);
47         std::swap(size_, move.size_);
48     }
49 
50     Block& operator=(const Block& copy) = delete;
51     Block& operator=(Block&& move) noexcept {
52         clear();
53 
54         std::swap(data_, move.data_);
55         std::swap(capacity_, move.capacity_);
56         std::swap(size_, move.size_);
57 
58         return *this;
59     }
60 
~BlockBlock61     ~Block() { clear(); }
62 
resizeBlock63     void resize(size_t new_size) {
64         if (!data_) {
65             allocate(new_size);
66         } else {
67             CHECK_GE(capacity_, new_size);
68             size_ = new_size;
69         }
70     }
71 
72     template <typename InputIt>
assignBlock73     void assign(InputIt begin, InputIt end) {
74         clear();
75         allocate(end - begin);
76         std::copy(begin, end, data_.get());
77     }
78 
clearBlock79     void clear() {
80         data_.reset();
81         capacity_ = 0;
82         size_ = 0;
83     }
84 
capacityBlock85     size_t capacity() const { return capacity_; }
sizeBlock86     size_t size() const { return size_; }
emptyBlock87     bool empty() const { return size() == 0; }
88 
dataBlock89     char* data() { return data_.get(); }
dataBlock90     const char* data() const { return data_.get(); }
91 
beginBlock92     char* begin() { return data_.get(); }
beginBlock93     const char* begin() const { return data_.get(); }
94 
endBlock95     char* end() { return data() + size_; }
endBlock96     const char* end() const { return data() + size_; }
97 
98     char& operator[](size_t idx) { return data()[idx]; }
99     const char& operator[](size_t idx) const { return data()[idx]; }
100 
101     bool operator==(const Block& rhs) const {
102         return size() == rhs.size() && memcmp(data(), rhs.data(), size()) == 0;
103     }
104 
105   private:
allocateBlock106     void allocate(size_t size) {
107         CHECK(data_ == nullptr);
108         CHECK_EQ(0ULL, capacity_);
109         CHECK_EQ(0ULL, size_);
110         if (size != 0) {
111             // This isn't std::make_unique because that's equivalent to `new char[size]()`, which
112             // value-initializes the array instead of leaving it uninitialized. As an optimization,
113             // call new without parentheses to avoid this costly initialization.
114             data_.reset(new char[size]);
115             capacity_ = size;
116             size_ = size;
117         }
118     }
119 
120     std::unique_ptr<char[]> data_;
121     size_t capacity_ = 0;
122     size_t size_ = 0;
123 };
124 
125 struct amessage {
126     uint32_t command;     /* command identifier constant      */
127     uint32_t arg0;        /* first argument                   */
128     uint32_t arg1;        /* second argument                  */
129     uint32_t data_length; /* length of payload (0 is allowed) */
130     uint32_t data_check;  /* checksum of data payload         */
131     uint32_t magic;       /* command ^ 0xffffffff             */
132 };
133 
134 struct apacket {
135     using payload_type = Block;
136     amessage msg;
137     payload_type payload;
138 };
139 
140 struct IOVector {
141     using value_type = char;
142     using block_type = Block;
143     using size_type = size_t;
144 
IOVectorIOVector145     IOVector() {}
146 
IOVectorIOVector147     explicit IOVector(std::unique_ptr<block_type> block) {
148         append(std::move(block));
149     }
150 
151     IOVector(const IOVector& copy) = delete;
IOVectorIOVector152     IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
153 
154     IOVector& operator=(const IOVector& copy) = delete;
155     IOVector& operator=(IOVector&& move) noexcept {
156         chain_ = std::move(move.chain_);
157         chain_length_ = move.chain_length_;
158         begin_offset_ = move.begin_offset_;
159         end_offset_ = move.end_offset_;
160 
161         move.chain_.clear();
162         move.chain_length_ = 0;
163         move.begin_offset_ = 0;
164         move.end_offset_ = 0;
165 
166         return *this;
167     }
168 
sizeIOVector169     size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
emptyIOVector170     bool empty() const { return size() == 0; }
171 
clearIOVector172     void clear() {
173         chain_length_ = 0;
174         begin_offset_ = 0;
175         end_offset_ = 0;
176         chain_.clear();
177     }
178 
179     // Split the first |len| bytes out of this chain into its own.
take_frontIOVector180     IOVector take_front(size_type len) {
181         IOVector head;
182 
183         if (len == 0) {
184             return head;
185         }
186         CHECK_GE(size(), len);
187 
188         std::shared_ptr<const block_type> first_block = chain_.front();
189         CHECK_GE(first_block->size(), begin_offset_);
190         head.append_shared(std::move(first_block));
191         head.begin_offset_ = begin_offset_;
192 
193         while (head.size() < len) {
194             pop_front_block();
195             CHECK(!chain_.empty());
196 
197             head.append_shared(chain_.front());
198         }
199 
200         if (head.size() == len) {
201             // Head takes full ownership of the last block it took.
202             head.end_offset_ = 0;
203             begin_offset_ = 0;
204             pop_front_block();
205         } else {
206             // Head takes partial ownership of the last block it took.
207             size_t bytes_taken = head.size() - len;
208             head.end_offset_ = bytes_taken;
209             CHECK_GE(chain_.front()->size(), bytes_taken);
210             begin_offset_ = chain_.front()->size() - bytes_taken;
211         }
212 
213         return head;
214     }
215 
216     // Add a nonempty block to the chain.
217     // The end of the chain must be a complete block (i.e. end_offset_ == 0).
appendIOVector218     void append(std::unique_ptr<const block_type> block) {
219         if (block->size() == 0) {
220             return;
221         }
222 
223         CHECK_EQ(0ULL, end_offset_);
224         chain_length_ += block->size();
225         chain_.emplace_back(std::move(block));
226     }
227 
appendIOVector228     void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
229 
trim_frontIOVector230     void trim_front() {
231         if (begin_offset_ == 0) {
232             return;
233         }
234 
235         const block_type* first_block = chain_.front().get();
236         auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
237         memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
238         chain_.front() = std::move(copy);
239 
240         chain_length_ -= begin_offset_;
241         begin_offset_ = 0;
242     }
243 
244   private:
245     // append, except takes a shared_ptr.
246     // Private to prevent exterior mutation of blocks.
append_sharedIOVector247     void append_shared(std::shared_ptr<const block_type> block) {
248         CHECK_NE(0ULL, block->size());
249         CHECK_EQ(0ULL, end_offset_);
250         chain_length_ += block->size();
251         chain_.emplace_back(std::move(block));
252     }
253 
254     // Drop the front block from the chain, and update chain_length_ appropriately.
pop_front_blockIOVector255     void pop_front_block() {
256         chain_length_ -= chain_.front()->size();
257         begin_offset_ = 0;
258         chain_.pop_front();
259     }
260 
261     // Iterate over the blocks with a callback with an operator()(const char*, size_t).
262     template <typename Fn>
iterate_blocksIOVector263     void iterate_blocks(Fn&& callback) const {
264         if (chain_.size() == 0) {
265             return;
266         }
267 
268         for (size_t i = 0; i < chain_.size(); ++i) {
269             const std::shared_ptr<const block_type>& block = chain_.at(i);
270             const char* begin = block->data();
271             size_t length = block->size();
272 
273             // Note that both of these conditions can be true if there's only one block.
274             if (i == 0) {
275                 CHECK_GE(block->size(), begin_offset_);
276                 begin += begin_offset_;
277                 length -= begin_offset_;
278             }
279 
280             if (i == chain_.size() - 1) {
281                 CHECK_GE(length, end_offset_);
282                 length -= end_offset_;
283             }
284 
285             callback(begin, length);
286         }
287     }
288 
289   public:
290     // Copy all of the blocks into a single block.
291     template <typename CollectionType = block_type>
coalesceIOVector292     CollectionType coalesce() const {
293         CollectionType result;
294         if (size() == 0) {
295             return result;
296         }
297 
298         result.resize(size());
299 
300         size_t offset = 0;
301         iterate_blocks([&offset, &result](const char* data, size_t len) {
302             memcpy(&result[offset], data, len);
303             offset += len;
304         });
305 
306         return result;
307     }
308 
309     template <typename FunctionType>
310     auto coalesced(FunctionType&& f) const ->
311         typename std::result_of<FunctionType(const char*, size_t)>::type {
312         if (chain_.size() == 1) {
313             // If we only have one block, we can use it directly.
314             return f(chain_.front()->data() + begin_offset_, size());
315         } else {
316             // Otherwise, copy to a single block.
317             auto data = coalesce();
318             return f(data.data(), data.size());
319         }
320     }
321 
322     // Get a list of iovecs that can be used to write out all of the blocks.
iovecsIOVector323     std::vector<adb_iovec> iovecs() const {
324         std::vector<adb_iovec> result;
325         iterate_blocks([&result](const char* data, size_t len) {
326             adb_iovec iov;
327             iov.iov_base = const_cast<char*>(data);
328             iov.iov_len = len;
329             result.emplace_back(iov);
330         });
331 
332         return result;
333     }
334 
335   private:
336     // Total length of all of the blocks in the chain.
337     size_t chain_length_ = 0;
338 
339     size_t begin_offset_ = 0;
340     size_t end_offset_ = 0;
341     std::deque<std::shared_ptr<const block_type>> chain_;
342 };
343