// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include #include #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" namespace Service::android { struct ParcelHeader { u32 data_size; u32 data_offset; u32 objects_size; u32 objects_offset; }; static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size"); class InputParcel final { public: explicit InputParcel(std::span in_data) : read_buffer(std::move(in_data)) { DeserializeHeader(); [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); } template void Read(T& val) { static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); ASSERT(read_index + sizeof(T) <= read_buffer.size()); std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); read_index = Common::AlignUp(read_index, 4); } template T Read() { T val; Read(val); return val; } template void ReadFlattened(T& val) { const auto flattened_size = Read(); ASSERT(sizeof(T) == flattened_size); Read(val); } template T ReadFlattened() { T val; ReadFlattened(val); return val; } template T ReadUnaligned() { static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); ASSERT(read_index + sizeof(T) <= read_buffer.size()); T val; std::memcpy(&val, read_buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); return val; } template const std::shared_ptr ReadObject() { static_assert(std::is_trivially_copyable_v, "T must be trivially copyable."); const auto is_valid{Read()}; if (is_valid) { auto result = std::make_shared(); ReadFlattened(*result); return result; } return {}; } std::u16string ReadInterfaceToken() { [[maybe_unused]] const u32 unknown = Read(); const u32 length = Read(); std::u16string token; token.reserve(length + 1); for (u32 ch = 0; ch < length + 1; ++ch) { token.push_back(ReadUnaligned()); } read_index = Common::AlignUp(read_index, 4); return token; } void DeserializeHeader() { ASSERT(read_buffer.size() > sizeof(ParcelHeader)); ParcelHeader header{}; std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader)); read_index = header.data_offset; } private: std::span read_buffer; std::size_t read_index = 0; }; class OutputParcel final { public: OutputParcel() = default; template void Write(const T& val) { this->WriteImpl(val, m_data_buffer); } template void WriteFlattenedObject(const T* ptr) { if (!ptr) { this->Write(0); return; } this->Write(1); this->Write(sizeof(T)); this->Write(*ptr); } template void WriteFlattenedObject(const std::shared_ptr ptr) { this->WriteFlattenedObject(ptr.get()); } template void WriteInterface(const T& val) { this->WriteImpl(val, m_data_buffer); this->WriteImpl(0U, m_object_buffer); } std::vector Serialize() const { std::vector output_buffer(sizeof(ParcelHeader) + m_data_buffer.size() + m_object_buffer.size()); ParcelHeader header{}; header.data_size = static_cast(m_data_buffer.size()); header.data_offset = sizeof(ParcelHeader); header.objects_size = static_cast(m_object_buffer.size()); header.objects_offset = header.data_offset + header.data_size; std::memcpy(output_buffer.data(), &header, sizeof(header)); std::ranges::copy(m_data_buffer, output_buffer.data() + header.data_offset); std::ranges::copy(m_object_buffer, output_buffer.data() + header.objects_offset); return output_buffer; } private: template requires(std::is_trivially_copyable_v) void WriteImpl(const T& val, std::vector& buffer) { const size_t aligned_size = Common::AlignUp(sizeof(T), 4); const size_t old_size = buffer.size(); buffer.resize(old_size + aligned_size); std::memcpy(buffer.data() + old_size, &val, sizeof(T)); } private: std::vector m_data_buffer; std::vector m_object_buffer; }; } // namespace Service::android