| /* |
| * Copyright (C) 2018 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. |
| */ |
| |
| #pragma once |
| |
| #include <algorithm> |
| #include <deque> |
| #include <memory> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include <android-base/logging.h> |
| |
| #include "sysdeps/uio.h" |
| |
| // Essentially std::vector<char>, except without zero initialization or reallocation. |
| struct Block { |
| using iterator = char*; |
| |
| Block() {} |
| |
| explicit Block(size_t size) { allocate(size); } |
| |
| template <typename Iterator> |
| Block(Iterator begin, Iterator end) : Block(end - begin) { |
| std::copy(begin, end, data_.get()); |
| } |
| |
| Block(const Block& copy) = delete; |
| Block(Block&& move) noexcept { |
| std::swap(data_, move.data_); |
| std::swap(capacity_, move.capacity_); |
| std::swap(size_, move.size_); |
| } |
| |
| Block& operator=(const Block& copy) = delete; |
| Block& operator=(Block&& move) noexcept { |
| clear(); |
| |
| std::swap(data_, move.data_); |
| std::swap(capacity_, move.capacity_); |
| std::swap(size_, move.size_); |
| |
| return *this; |
| } |
| |
| ~Block() { clear(); } |
| |
| void resize(size_t new_size) { |
| if (!data_) { |
| allocate(new_size); |
| } else { |
| CHECK_GE(capacity_, new_size); |
| size_ = new_size; |
| } |
| } |
| |
| template <typename InputIt> |
| void assign(InputIt begin, InputIt end) { |
| clear(); |
| allocate(end - begin); |
| std::copy(begin, end, data_.get()); |
| } |
| |
| void clear() { |
| data_.reset(); |
| capacity_ = 0; |
| size_ = 0; |
| } |
| |
| size_t capacity() const { return capacity_; } |
| size_t size() const { return size_; } |
| bool empty() const { return size() == 0; } |
| |
| char* data() { return data_.get(); } |
| const char* data() const { return data_.get(); } |
| |
| char* begin() { return data_.get(); } |
| const char* begin() const { return data_.get(); } |
| |
| char* end() { return data() + size_; } |
| const char* end() const { return data() + size_; } |
| |
| char& operator[](size_t idx) { return data()[idx]; } |
| const char& operator[](size_t idx) const { return data()[idx]; } |
| |
| bool operator==(const Block& rhs) const { |
| return size() == rhs.size() && memcmp(data(), rhs.data(), size()) == 0; |
| } |
| |
| private: |
| void allocate(size_t size) { |
| CHECK(data_ == nullptr); |
| CHECK_EQ(0ULL, capacity_); |
| CHECK_EQ(0ULL, size_); |
| if (size != 0) { |
| // This isn't std::make_unique because that's equivalent to `new char[size]()`, which |
| // value-initializes the array instead of leaving it uninitialized. As an optimization, |
| // call new without parentheses to avoid this costly initialization. |
| data_.reset(new char[size]); |
| capacity_ = size; |
| size_ = size; |
| } |
| } |
| |
| std::unique_ptr<char[]> data_; |
| size_t capacity_ = 0; |
| size_t size_ = 0; |
| }; |
| |
| struct amessage { |
| uint32_t command; /* command identifier constant */ |
| uint32_t arg0; /* first argument */ |
| uint32_t arg1; /* second argument */ |
| uint32_t data_length; /* length of payload (0 is allowed) */ |
| uint32_t data_check; /* checksum of data payload */ |
| uint32_t magic; /* command ^ 0xffffffff */ |
| }; |
| |
| struct apacket { |
| using payload_type = Block; |
| amessage msg; |
| payload_type payload; |
| }; |
| |
| struct IOVector { |
| using value_type = char; |
| using block_type = Block; |
| using size_type = size_t; |
| |
| IOVector() {} |
| |
| explicit IOVector(std::unique_ptr<block_type> block) { |
| append(std::move(block)); |
| } |
| |
| IOVector(const IOVector& copy) = delete; |
| IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); } |
| |
| IOVector& operator=(const IOVector& copy) = delete; |
| IOVector& operator=(IOVector&& move) noexcept { |
| chain_ = std::move(move.chain_); |
| chain_length_ = move.chain_length_; |
| begin_offset_ = move.begin_offset_; |
| end_offset_ = move.end_offset_; |
| |
| move.chain_.clear(); |
| move.chain_length_ = 0; |
| move.begin_offset_ = 0; |
| move.end_offset_ = 0; |
| |
| return *this; |
| } |
| |
| size_type size() const { return chain_length_ - begin_offset_ - end_offset_; } |
| bool empty() const { return size() == 0; } |
| |
| void clear() { |
| chain_length_ = 0; |
| begin_offset_ = 0; |
| end_offset_ = 0; |
| chain_.clear(); |
| } |
| |
| // Split the first |len| bytes out of this chain into its own. |
| IOVector take_front(size_type len) { |
| IOVector head; |
| |
| if (len == 0) { |
| return head; |
| } |
| CHECK_GE(size(), len); |
| |
| std::shared_ptr<const block_type> first_block = chain_.front(); |
| CHECK_GE(first_block->size(), begin_offset_); |
| head.append_shared(std::move(first_block)); |
| head.begin_offset_ = begin_offset_; |
| |
| while (head.size() < len) { |
| pop_front_block(); |
| CHECK(!chain_.empty()); |
| |
| head.append_shared(chain_.front()); |
| } |
| |
| if (head.size() == len) { |
| // Head takes full ownership of the last block it took. |
| head.end_offset_ = 0; |
| begin_offset_ = 0; |
| pop_front_block(); |
| } else { |
| // Head takes partial ownership of the last block it took. |
| size_t bytes_taken = head.size() - len; |
| head.end_offset_ = bytes_taken; |
| CHECK_GE(chain_.front()->size(), bytes_taken); |
| begin_offset_ = chain_.front()->size() - bytes_taken; |
| } |
| |
| return head; |
| } |
| |
| // Add a nonempty block to the chain. |
| // The end of the chain must be a complete block (i.e. end_offset_ == 0). |
| void append(std::unique_ptr<const block_type> block) { |
| if (block->size() == 0) { |
| return; |
| } |
| |
| CHECK_EQ(0ULL, end_offset_); |
| chain_length_ += block->size(); |
| chain_.emplace_back(std::move(block)); |
| } |
| |
| void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); } |
| |
| void trim_front() { |
| if (begin_offset_ == 0) { |
| return; |
| } |
| |
| const block_type* first_block = chain_.front().get(); |
| auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_); |
| memcpy(copy->data(), first_block->data() + begin_offset_, copy->size()); |
| chain_.front() = std::move(copy); |
| |
| chain_length_ -= begin_offset_; |
| begin_offset_ = 0; |
| } |
| |
| private: |
| // append, except takes a shared_ptr. |
| // Private to prevent exterior mutation of blocks. |
| void append_shared(std::shared_ptr<const block_type> block) { |
| CHECK_NE(0ULL, block->size()); |
| CHECK_EQ(0ULL, end_offset_); |
| chain_length_ += block->size(); |
| chain_.emplace_back(std::move(block)); |
| } |
| |
| // Drop the front block from the chain, and update chain_length_ appropriately. |
| void pop_front_block() { |
| chain_length_ -= chain_.front()->size(); |
| begin_offset_ = 0; |
| chain_.pop_front(); |
| } |
| |
| // Iterate over the blocks with a callback with an operator()(const char*, size_t). |
| template <typename Fn> |
| void iterate_blocks(Fn&& callback) const { |
| if (chain_.size() == 0) { |
| return; |
| } |
| |
| for (size_t i = 0; i < chain_.size(); ++i) { |
| const std::shared_ptr<const block_type>& block = chain_.at(i); |
| const char* begin = block->data(); |
| size_t length = block->size(); |
| |
| // Note that both of these conditions can be true if there's only one block. |
| if (i == 0) { |
| CHECK_GE(block->size(), begin_offset_); |
| begin += begin_offset_; |
| length -= begin_offset_; |
| } |
| |
| if (i == chain_.size() - 1) { |
| CHECK_GE(length, end_offset_); |
| length -= end_offset_; |
| } |
| |
| callback(begin, length); |
| } |
| } |
| |
| public: |
| // Copy all of the blocks into a single block. |
| template <typename CollectionType = block_type> |
| CollectionType coalesce() const { |
| CollectionType result; |
| if (size() == 0) { |
| return result; |
| } |
| |
| result.resize(size()); |
| |
| size_t offset = 0; |
| iterate_blocks([&offset, &result](const char* data, size_t len) { |
| memcpy(&result[offset], data, len); |
| offset += len; |
| }); |
| |
| return result; |
| } |
| |
| template <typename FunctionType> |
| auto coalesced(FunctionType&& f) const -> |
| typename std::result_of<FunctionType(const char*, size_t)>::type { |
| if (chain_.size() == 1) { |
| // If we only have one block, we can use it directly. |
| return f(chain_.front()->data() + begin_offset_, size()); |
| } else { |
| // Otherwise, copy to a single block. |
| auto data = coalesce(); |
| return f(data.data(), data.size()); |
| } |
| } |
| |
| // Get a list of iovecs that can be used to write out all of the blocks. |
| std::vector<adb_iovec> iovecs() const { |
| std::vector<adb_iovec> result; |
| 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; |
| } |
| |
| private: |
| // Total length of all of the blocks in the chain. |
| size_t chain_length_ = 0; |
| |
| size_t begin_offset_ = 0; |
| size_t end_offset_ = 0; |
| std::deque<std::shared_ptr<const block_type>> chain_; |
| }; |