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