359 lines
9.7 KiB
C
359 lines
9.7 KiB
C
|
/*
|
||
|
* 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 <string.h>
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <memory>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
#include <android-base/logging.h>
|
||
|
|
||
|
#include "fdevent/fdevent.h"
|
||
|
#include "sysdeps/uio.h"
|
||
|
|
||
|
// Essentially std::vector<char>, except without zero initialization or reallocation.
|
||
|
struct Block {
|
||
|
using iterator = char*;
|
||
|
|
||
|
Block() = default;
|
||
|
|
||
|
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
|
||
|
: data_(std::exchange(move.data_, nullptr)),
|
||
|
capacity_(std::exchange(move.capacity_, 0)),
|
||
|
size_(std::exchange(move.size_, 0)) {}
|
||
|
|
||
|
Block& operator=(const Block& copy) = delete;
|
||
|
Block& operator=(Block&& move) noexcept {
|
||
|
clear();
|
||
|
data_ = std::exchange(move.data_, nullptr);
|
||
|
capacity_ = std::exchange(move.capacity_, 0);
|
||
|
size_ = std::exchange(move.size_, 0);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
~Block() = default;
|
||
|
|
||
|
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() = default;
|
||
|
|
||
|
explicit IOVector(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;
|
||
|
|
||
|
const value_type* front_data() const {
|
||
|
if (chain_.empty()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
return chain_[start_index_].data() + begin_offset_;
|
||
|
}
|
||
|
|
||
|
size_type front_size() const {
|
||
|
if (chain_.empty()) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return chain_[start_index_].size() - begin_offset_;
|
||
|
}
|
||
|
|
||
|
size_type size() const { return chain_length_ - begin_offset_; }
|
||
|
bool empty() const { return size() == 0; }
|
||
|
|
||
|
// Return the last block so the caller can still reuse its allocated capacity
|
||
|
// or it can be simply ignored.
|
||
|
block_type clear();
|
||
|
|
||
|
void drop_front(size_type len);
|
||
|
|
||
|
// Split the first |len| bytes out of this chain into its own.
|
||
|
IOVector take_front(size_type len);
|
||
|
|
||
|
// Add a nonempty block to the chain.
|
||
|
void append(block_type&& block) {
|
||
|
if (block.size() == 0) {
|
||
|
return;
|
||
|
}
|
||
|
CHECK_NE(0ULL, block.size());
|
||
|
chain_length_ += block.size();
|
||
|
chain_.emplace_back(std::move(block));
|
||
|
}
|
||
|
|
||
|
void trim_front();
|
||
|
|
||
|
private:
|
||
|
void trim_chain_front();
|
||
|
|
||
|
// Drop the front block from the chain, and update chain_length_ appropriately.
|
||
|
void pop_front_block();
|
||
|
|
||
|
// Iterate over the blocks with a callback with an operator()(const char*, size_t).
|
||
|
template <typename Fn>
|
||
|
void iterate_blocks(Fn&& callback) const {
|
||
|
if (size() == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (size_t i = start_index_; i < chain_.size(); ++i) {
|
||
|
const auto& block = chain_[i];
|
||
|
const char* begin = block.data();
|
||
|
size_t length = block.size();
|
||
|
|
||
|
if (i == start_index_) {
|
||
|
CHECK_GE(block.size(), begin_offset_);
|
||
|
begin += begin_offset_;
|
||
|
length -= begin_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;
|
||
|
}
|
||
|
|
||
|
block_type coalesce() &&;
|
||
|
|
||
|
template <typename FunctionType>
|
||
|
auto coalesced(FunctionType&& f) const {
|
||
|
if (chain_.size() == start_index_ + 1) {
|
||
|
// If we only have one block, we can use it directly.
|
||
|
return f(chain_[start_index_].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;
|
||
|
|
||
|
private:
|
||
|
// Total length of all of the blocks in the chain.
|
||
|
size_t chain_length_ = 0;
|
||
|
|
||
|
size_t begin_offset_ = 0;
|
||
|
size_t start_index_ = 0;
|
||
|
std::vector<block_type> chain_;
|
||
|
};
|
||
|
|
||
|
// An implementation of weak pointers tied to the fdevent run loop.
|
||
|
//
|
||
|
// This allows for code to submit a request for an object, and upon receiving
|
||
|
// a response, know whether the object is still alive, or has been destroyed
|
||
|
// because of other reasons. We keep a list of living weak_ptrs in each object,
|
||
|
// and clear the weak_ptrs when the object is destroyed. This is safe, because
|
||
|
// we require that both the destructor of the referent and the get method on
|
||
|
// the weak_ptr are executed on the main thread.
|
||
|
template <typename T>
|
||
|
struct enable_weak_from_this;
|
||
|
|
||
|
template <typename T>
|
||
|
struct weak_ptr {
|
||
|
weak_ptr() = default;
|
||
|
explicit weak_ptr(T* ptr) { reset(ptr); }
|
||
|
weak_ptr(const weak_ptr& copy) { reset(copy.get()); }
|
||
|
|
||
|
weak_ptr(weak_ptr&& move) {
|
||
|
reset(move.get());
|
||
|
move.reset();
|
||
|
}
|
||
|
|
||
|
~weak_ptr() { reset(); }
|
||
|
|
||
|
weak_ptr& operator=(const weak_ptr& copy) {
|
||
|
if (© == this) {
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
reset(copy.get());
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
weak_ptr& operator=(weak_ptr&& move) {
|
||
|
if (&move == this) {
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
reset(move.get());
|
||
|
move.reset();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
T* get() const {
|
||
|
check_main_thread();
|
||
|
return ptr_;
|
||
|
}
|
||
|
|
||
|
void reset(T* ptr = nullptr) {
|
||
|
check_main_thread();
|
||
|
|
||
|
if (ptr == ptr_) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ptr_) {
|
||
|
ptr_->weak_ptrs_.erase(
|
||
|
std::remove(ptr_->weak_ptrs_.begin(), ptr_->weak_ptrs_.end(), this));
|
||
|
}
|
||
|
|
||
|
ptr_ = ptr;
|
||
|
if (ptr_) {
|
||
|
ptr_->weak_ptrs_.push_back(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
friend struct enable_weak_from_this<T>;
|
||
|
T* ptr_ = nullptr;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
struct enable_weak_from_this {
|
||
|
~enable_weak_from_this() {
|
||
|
if (!weak_ptrs_.empty()) {
|
||
|
check_main_thread();
|
||
|
for (auto& weak : weak_ptrs_) {
|
||
|
weak->ptr_ = nullptr;
|
||
|
}
|
||
|
weak_ptrs_.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
weak_ptr<T> weak() { return weak_ptr<T>(static_cast<T*>(this)); }
|
||
|
|
||
|
void schedule_deletion() {
|
||
|
fdevent_run_on_main_thread([this]() { delete static_cast<T*>(this); });
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
friend struct weak_ptr<T>;
|
||
|
std::vector<weak_ptr<T>*> weak_ptrs_;
|
||
|
};
|