| #ifndef ANDROID_PDX_UTILITY_H_ |
| #define ANDROID_PDX_UTILITY_H_ |
| |
| #include <cstdint> |
| #include <iterator> |
| |
| #include <pdx/rpc/sequence.h> |
| |
| // Utilities for testing object serialization. |
| |
| namespace android { |
| namespace pdx { |
| |
| class ByteBuffer { |
| public: |
| using iterator = uint8_t*; |
| using const_iterator = const uint8_t*; |
| using size_type = size_t; |
| |
| ByteBuffer() = default; |
| ByteBuffer(const ByteBuffer& other) { |
| resize(other.size()); |
| if (other.size()) |
| memcpy(data_, other.data(), other.size()); |
| } |
| |
| ByteBuffer& operator=(const ByteBuffer& other) { |
| resize(other.size()); |
| if (other.size()) |
| memcpy(data_, other.data(), other.size()); |
| return *this; |
| } |
| |
| ByteBuffer& operator=(ByteBuffer&& other) { |
| std::swap(data_, other.data_); |
| std::swap(size_, other.size_); |
| std::swap(capacity_, other.capacity_); |
| other.clear(); |
| return *this; |
| } |
| |
| inline const uint8_t* data() const { return data_; } |
| inline uint8_t* data() { return data_; } |
| inline size_t size() const { return size_; } |
| inline size_t capacity() const { return capacity_; } |
| |
| iterator begin() { return data_; } |
| const_iterator begin() const { return data_; } |
| iterator end() { return data_ + size_; } |
| const_iterator end() const { return data_ + size_; } |
| |
| inline bool operator==(const ByteBuffer& other) const { |
| return size_ == other.size_ && |
| (size_ == 0 || memcmp(data_, other.data_, size_) == 0); |
| } |
| |
| inline bool operator!=(const ByteBuffer& other) const { |
| return !operator==(other); |
| } |
| |
| inline void reserve(size_t size) { |
| if (size <= capacity_) |
| return; |
| // Find next power of 2 (assuming the size is 32 bits for now). |
| size--; |
| size |= size >> 1; |
| size |= size >> 2; |
| size |= size >> 4; |
| size |= size >> 8; |
| size |= size >> 16; |
| size++; |
| void* new_data = data_ ? realloc(data_, size) : malloc(size); |
| // TODO(avakulenko): Check for allocation failures. |
| data_ = static_cast<uint8_t*>(new_data); |
| capacity_ = size; |
| } |
| |
| inline void resize(size_t size) { |
| reserve(size); |
| size_ = size; |
| } |
| |
| inline uint8_t* grow_by(size_t size_delta) { |
| size_t old_size = size_; |
| resize(old_size + size_delta); |
| return data_ + old_size; |
| } |
| |
| inline void clear() { size_ = 0; } |
| |
| private: |
| uint8_t* data_{nullptr}; |
| size_t size_{0}; |
| size_t capacity_{0}; |
| }; |
| |
| // Utility functions to increment/decrement void pointers to data buffers. |
| template <typename OFFSET_T> |
| inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) { |
| return static_cast<const uint8_t*>(ptr) + offset; |
| } |
| |
| template <typename OFFSET_T> |
| inline void* AdvancePointer(void* ptr, OFFSET_T offset) { |
| return static_cast<uint8_t*>(ptr) + offset; |
| } |
| |
| inline ptrdiff_t PointerDistance(const void* end, const void* begin) { |
| return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin); |
| } |
| |
| // Utility to build sequences of types. |
| template <typename, typename> |
| struct AppendTypeSequence; |
| |
| template <typename T, typename... S, template <typename...> class TT> |
| struct AppendTypeSequence<T, TT<S...>> { |
| using type = TT<S..., T>; |
| }; |
| |
| // Utility to generate repeated types. |
| template <typename T, std::size_t N, template <typename...> class TT> |
| struct RepeatedType { |
| using type = typename AppendTypeSequence< |
| T, typename RepeatedType<T, N - 1, TT>::type>::type; |
| }; |
| |
| template <typename T, template <typename...> class TT> |
| struct RepeatedType<T, 0, TT> { |
| using type = TT<>; |
| }; |
| |
| template <typename V, typename S> |
| inline V ReturnValueHelper(V value, S /*ignore*/) { |
| return value; |
| } |
| |
| template <typename R, typename V, size_t... S> |
| inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) { |
| return std::make_tuple(ReturnValueHelper(value, S)...); |
| } |
| |
| // Returns an N-tuple of type std::tuple<T,...T> containing |value| in each |
| // element. |
| template <size_t N, typename T, |
| typename R = typename RepeatedType<T, N, std::tuple>::type> |
| inline R GetNTuple(T value) { |
| return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{}); |
| } |
| |
| class NoOpOutputResourceMapper : public OutputResourceMapper { |
| public: |
| Status<FileReference> PushFileHandle(const LocalHandle& handle) override { |
| return handle.Get(); |
| } |
| |
| Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override { |
| return handle.Get(); |
| } |
| |
| Status<FileReference> PushFileHandle(const RemoteHandle& handle) override { |
| return handle.Get(); |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const LocalChannelHandle& handle) override { |
| return handle.value(); |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const BorrowedChannelHandle& handle) override { |
| return handle.value(); |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const RemoteChannelHandle& handle) override { |
| return handle.value(); |
| } |
| }; |
| |
| class NoOpInputResourceMapper : public InputResourceMapper { |
| public: |
| bool GetFileHandle(FileReference ref, LocalHandle* handle) override { |
| *handle = LocalHandle{ref}; |
| return true; |
| } |
| |
| bool GetChannelHandle(ChannelReference ref, |
| LocalChannelHandle* handle) override { |
| *handle = LocalChannelHandle{nullptr, ref}; |
| return true; |
| } |
| }; |
| |
| class NoOpResourceMapper : public NoOpOutputResourceMapper, |
| public NoOpInputResourceMapper {}; |
| |
| // Simple implementation of the payload interface, required by |
| // Serialize/Deserialize. This is intended for test cases, where compatibility |
| // with std::vector is helpful. |
| class Payload : public MessageWriter, |
| public MessageReader, |
| public OutputResourceMapper { |
| public: |
| using BaseType = ByteBuffer; |
| using iterator = typename BaseType::iterator; |
| using const_iterator = typename BaseType::const_iterator; |
| using size_type = typename BaseType::size_type; |
| |
| Payload() = default; |
| explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); } |
| Payload(const Payload& other) : buffer_(other.buffer_) {} |
| Payload(const std::initializer_list<uint8_t>& initializer) { |
| buffer_.resize(initializer.size()); |
| std::copy(initializer.begin(), initializer.end(), buffer_.begin()); |
| } |
| |
| Payload& operator=(const Payload& other) { |
| buffer_ = other.buffer_; |
| read_pos_ = 0; |
| return *this; |
| } |
| Payload& operator=(const std::initializer_list<uint8_t>& initializer) { |
| buffer_.resize(initializer.size()); |
| std::copy(initializer.begin(), initializer.end(), buffer_.begin()); |
| read_pos_ = 0; |
| return *this; |
| } |
| |
| // Compares Payload with Payload. |
| bool operator==(const Payload& other) const { |
| return buffer_ == other.buffer_; |
| } |
| bool operator!=(const Payload& other) const { |
| return buffer_ != other.buffer_; |
| } |
| |
| // Compares Payload with std::vector. |
| template <typename Type, typename AllocatorType> |
| typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type |
| operator==(const std::vector<Type, AllocatorType>& other) const { |
| return buffer_.size() == other.size() && |
| memcmp(buffer_.data(), other.data(), other.size()) == 0; |
| } |
| template <typename Type, typename AllocatorType> |
| typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type |
| operator!=(const std::vector<Type, AllocatorType>& other) const { |
| return !operator!=(other); |
| } |
| |
| iterator begin() { return buffer_.begin(); } |
| const_iterator begin() const { return buffer_.begin(); } |
| iterator end() { return buffer_.end(); } |
| const_iterator end() const { return buffer_.end(); } |
| |
| void Append(size_type count, uint8_t value) { |
| auto* data = buffer_.grow_by(count); |
| std::fill(data, data + count, value); |
| } |
| |
| void Clear() { |
| buffer_.clear(); |
| file_handles_.clear(); |
| read_pos_ = 0; |
| } |
| |
| void Rewind() { read_pos_ = 0; } |
| |
| uint8_t* Data() { return buffer_.data(); } |
| const uint8_t* Data() const { return buffer_.data(); } |
| size_type Size() const { return buffer_.size(); } |
| |
| // MessageWriter |
| void* GetNextWriteBufferSection(size_t size) override { |
| return buffer_.grow_by(size); |
| } |
| |
| OutputResourceMapper* GetOutputResourceMapper() override { return this; } |
| |
| // OutputResourceMapper |
| Status<FileReference> PushFileHandle(const LocalHandle& handle) override { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.Get()); |
| return ref; |
| } else { |
| return handle.Get(); |
| } |
| } |
| |
| Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.Get()); |
| return ref; |
| } else { |
| return handle.Get(); |
| } |
| } |
| |
| Status<FileReference> PushFileHandle(const RemoteHandle& handle) override { |
| return handle.Get(); |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const LocalChannelHandle& handle) override { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.value()); |
| return ref; |
| } else { |
| return handle.value(); |
| } |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const BorrowedChannelHandle& handle) override { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.value()); |
| return ref; |
| } else { |
| return handle.value(); |
| } |
| } |
| |
| Status<ChannelReference> PushChannelHandle( |
| const RemoteChannelHandle& handle) override { |
| return handle.value(); |
| } |
| |
| // MessageReader |
| BufferSection GetNextReadBufferSection() override { |
| return {buffer_.data() + read_pos_, &*buffer_.end()}; |
| } |
| |
| void ConsumeReadBufferSectionData(const void* new_start) override { |
| read_pos_ = PointerDistance(new_start, buffer_.data()); |
| } |
| |
| InputResourceMapper* GetInputResourceMapper() override { |
| return &input_resource_mapper_; |
| } |
| |
| const int* FdArray() const { return file_handles_.data(); } |
| std::size_t FdCount() const { return file_handles_.size(); } |
| |
| private: |
| NoOpInputResourceMapper input_resource_mapper_; |
| ByteBuffer buffer_; |
| std::vector<int> file_handles_; |
| size_t read_pos_{0}; |
| }; |
| |
| } // namespace pdx |
| } // namespace android |
| |
| // Helper macros for branch prediction hinting. |
| #ifdef __GNUC__ |
| #define PDX_LIKELY(x) __builtin_expect(!!(x), true) |
| #define PDX_UNLIKELY(x) __builtin_expect(!!(x), false) |
| #else |
| #define PDX_LIKELY(x) (x) |
| #define PDX_UNLIKELY(x) (x) |
| #endif |
| |
| #endif // ANDROID_PDX_UTILITY_H_ |