rpcsx/rx/include/rx/Serializer.hpp
DH d7ad77b406 orbis: implement physical memory emulation level & utils improvement
fix blockpool & dmem implementation
modernize blockpool & dmem io devices
use budgets per allocation
add serialization support for MemoryTableWithPayload and AddressRange utils
add format support for EnumBitSet util
implemented trace formatter per syscall
increased allowed reference count for Ref
2025-11-30 15:46:37 +03:00

475 lines
14 KiB
C++

#pragma once
#include "rx/align.hpp"
#include "rx/refl.hpp"
#include <cassert>
#include <concepts>
#include <cstdint>
#include <cstring>
#include <span>
#include <tuple>
#include <type_traits>
#include <utility>
namespace rx {
struct Serializer;
struct Deserializer;
template <typename T> struct TypeSerializer;
namespace detail {
// TODO: replace with std::is_trivially_relocatable once available
template <typename T>
concept TriviallyRelocatable =
std::is_trivially_copyable_v<std::remove_cvref_t<T>> &&
std::is_trivially_default_constructible_v<std::remove_cvref_t<T>> &&
!std::is_pointer_v<T> && !std::is_reference_v<T>;
template <typename T>
concept TypeSerializable = requires(Serializer &s, const T &value) {
TypeSerializer<std::remove_cvref_t<T>>::serialize(s, value);
} && (requires(Deserializer &d) {
{
TypeSerializer<std::remove_cvref_t<T>>::deserialize(d)
} -> std::same_as<std::remove_cvref_t<T>>;
} || requires(Deserializer &d, T &value) {
TypeSerializer<std::remove_cvref_t<T>>::deserialize(d, value);
});
template <typename T>
concept IsRange = requires(T &object) {
object.size();
*object.begin();
object.begin() != object.end();
};
struct StructSerializerField {
std::size_t offset;
std::size_t alignment;
std::size_t size;
void (*serialize)(rx::Serializer &s, const void *object);
void (*deserialize)(rx::Deserializer &s, void *object);
};
// try to call free function
template <typename T>
void callSerializeFunction(Serializer &s, const T &value)
requires requires { serialize(s, value); }
{
serialize(s, value);
}
// try to call free function
template <typename T>
void callDeserializeFunction(Deserializer &s, T &value)
requires requires { deserialize(s, value); }
{
deserialize(s, value);
}
template <typename T>
requires(!std::is_array_v<T>)
T callDeserializeFunction(Deserializer &s)
requires requires(T &value) { deserialize<T>(s); }
{
return deserialize<T>(s);
}
template <typename T>
constexpr auto SerializableImpl = requires(Serializer &s, const T &value) {
value.serialize(s);
} || requires(Serializer &s, const T &value) {
callSerializeFunction(s, value);
} || requires(Serializer &s, const T &value) {
TypeSerializer<std::remove_cvref_t<T>>::serialize(s, value);
};
template <typename T>
constexpr auto DeserializableImpl = requires(Deserializer &d, T &value) {
value.deserialize(d);
} || requires(Deserializer &d, T &value) {
value = std::remove_cvref_t<T>::deserialize(d);
} || requires(Deserializer &d, T &value) {
callDeserializeFunction(d, value);
} || requires(Deserializer &d, T &value) {
value = callDeserializeFunction<std::remove_cvref_t<T>>(d);
} || requires(Deserializer &d) {
{
TypeSerializer<std::remove_cvref_t<T>>::deserialize(d)
} -> std::same_as<std::remove_cvref_t<T>>;
} || requires(Deserializer &d, T &value) {
TypeSerializer<std::remove_cvref_t<T>>::deserialize(d, value);
};
template <typename T>
constexpr auto IsSerializable = SerializableImpl<T> && DeserializableImpl<T>;
} // namespace detail
template <typename T>
concept Serializable = detail::IsSerializable<T>;
namespace detail {
template <typename ClassT> struct SerializableFieldTest {
template <typename FieldT>
requires(std::is_default_constructible_v<FieldT> &&
!std::is_same_v<std::remove_cvref_t<FieldT>, ClassT> &&
detail::IsSerializable<FieldT>)
constexpr operator FieldT();
};
struct SerializableAnyFieldTest {
template <typename FieldT> constexpr operator FieldT();
};
template <typename T> constexpr bool isSerializableFields() {
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
return requires { T{(I, SerializableFieldTest<T>{})...}; };
};
if constexpr (!std::is_class_v<T>) {
return false;
} else {
constexpr auto fieldCount = rx::fieldCount<T>;
if constexpr (fieldCount == 0) {
return false;
} else {
return impl(std::make_index_sequence<fieldCount>{});
}
}
}
template <typename T>
constexpr auto SerializableClass =
!detail::IsRange<T> && isSerializableFields<T>();
} // namespace detail
struct Serializer {
virtual ~Serializer() = default;
virtual void write(std::span<const std::byte> data) = 0;
template <Serializable T> void serialize(const T &value) {
if constexpr (requires { value.serialize(*this); }) {
value.serialize(*this);
} else if constexpr (requires { callSerializeFunction(*this, value); }) {
callSerializeFunction(*this, value);
} else {
TypeSerializer<std::remove_cvref_t<T>>::serialize(*this, value);
}
}
};
struct Deserializer {
virtual ~Deserializer() = default;
virtual void read(std::span<std::byte> data) = 0;
template <Serializable T> [[nodiscard]] std::remove_cvref_t<T> deserialize() {
using type = std::remove_cvref_t<T>;
if constexpr (requires {
{ type::deserialize(*this) } -> std::convertible_to<type>;
}) {
return type::deserialize(*this);
} else if constexpr (requires(type &result) {
type::deserialize(*this, result);
}) {
type result;
type::deserialize(*this, result);
return result;
} else if constexpr (requires(type &result) {
result.deserialize(*this);
}) {
type result;
result.deserialize(*this);
return result;
} else if constexpr (requires(type &result) {
type::deserialize(*this, result);
}) {
type result;
T::deserialize(*this, result);
return result;
} else if constexpr (requires {
detail::callDeserializeFunction<type>(*this);
}) {
return detail::callDeserializeFunction<type>(*this);
} else if constexpr (requires(type &value) {
detail::callDeserializeFunction(*this, value);
}) {
type result;
detail::callDeserializeFunction(*this, result);
return result;
} else if constexpr (requires {
TypeSerializer<type>::deserialize(*this);
}) {
return TypeSerializer<type>::deserialize(*this);
} else {
type result;
TypeSerializer<type>::deserialize(*this, result);
return result;
}
}
template <Serializable T> void deserialize(T &result) {
if constexpr (requires { T::deserialize(*this, result); }) {
T::deserialize(*this, result);
} else if constexpr (requires { result.deserialize(*this); }) {
result.deserialize(*this);
} else if constexpr (requires {
{ T::deserialize(*this) } -> std::convertible_to<T>;
}) {
result = T::deserialize(*this);
} else if constexpr (requires {
detail::callDeserializeFunction(*this, result);
}) {
detail::callDeserializeFunction(*this, result);
} else if constexpr (requires {
detail::callDeserializeFunction<T>(*this);
}) {
result = detail::callDeserializeFunction<T>(*this);
} else if constexpr (requires {
TypeSerializer<T>::deserialize(*this, result);
}) {
TypeSerializer<T>::deserialize(*this, result);
} else {
result = TypeSerializer<T>::deserialize(*this);
}
}
void setFailure() { mFailure = true; }
[[nodiscard]] bool failure() const { return mFailure; }
private:
bool mFailure = false;
};
template <detail::TriviallyRelocatable T>
requires std::is_array_v<T>
struct TypeSerializer<T> {
static void serialize(Serializer &s, const T &t) {
std::byte rawBytes[sizeof(T)];
std::memcpy(rawBytes, &t, sizeof(T));
s.write(rawBytes);
}
};
template <detail::TriviallyRelocatable T>
requires(!std::is_array_v<T>)
struct TypeSerializer<T> {
static void serialize(Serializer &s, const T &t) {
std::byte rawBytes[sizeof(T)];
std::memcpy(rawBytes, &t, sizeof(T));
s.write(rawBytes);
}
static T deserialize(Deserializer &s) {
alignas(T) std::byte rawBytes[sizeof(T)];
s.read(rawBytes);
return std::move(std::bit_cast<T>(rawBytes));
}
};
template <typename A, typename B>
requires detail::IsSerializable<A> && detail::IsSerializable<B>
struct TypeSerializer<std::pair<A, B>> {
static void serialize(Serializer &s, const std::pair<A, B> &t) {
s.serialize(t.first);
s.serialize(t.second);
}
static std::pair<A, B> deserialize(Deserializer &s) {
auto a = s.deserialize<A>();
auto b = s.deserialize<B>();
return {
std::move(a),
std::move(b),
};
}
};
template <typename... T>
requires(detail::IsSerializable<T> && ...)
struct TypeSerializer<std::tuple<T...>> {
static void serialize(Serializer &s, const std::tuple<T...> &t) {
std::apply([&s](auto &value) { s.serialize(value); }, t);
}
static std::tuple<T...> deserialize(Deserializer &s) {
return std::tuple<T...>{s.deserialize<T>()...};
}
};
template <typename T>
requires std::is_default_constructible_v<T> &&
(!detail::TriviallyRelocatable<T>) && requires(T &object) {
requires detail::IsSerializable<decltype(object.size())>;
requires detail::IsSerializable<decltype(*object.begin())>;
object.resize(1);
object.begin() != object.end();
}
struct TypeSerializer<T> {
using item_type = std::remove_cvref_t<decltype(*std::declval<T>().begin())>;
static void serialize(Serializer &s, const T &t) {
s.serialize(static_cast<std::uint32_t>(t.size()));
if constexpr (detail::TriviallyRelocatable<item_type> &&
requires { reinterpret_cast<const std::byte *>(t.data()); }) {
s.write({reinterpret_cast<const std::byte *>(t.data()),
t.size() * sizeof(item_type)});
} else {
for (auto &item : t) {
s.serialize(item);
}
}
}
static T deserialize(Deserializer &s) {
auto size = s.deserialize<std::uint32_t>();
T t;
t.resize(size);
if constexpr (detail::TriviallyRelocatable<item_type> &&
requires { reinterpret_cast<const std::byte *>(t.data()); }) {
s.read({reinterpret_cast<std::byte *>(t.data()),
t.size() * sizeof(item_type)});
} else {
for (auto &item : t) {
s.deserialize(item);
}
}
return t;
}
};
template <detail::IsRange T>
requires(
std::is_default_constructible_v<T> &&
(!detail::TriviallyRelocatable<T>) &&
requires(T &object) {
requires detail::IsSerializable<decltype(object.size())>;
requires detail::IsSerializable<decltype(*object.begin())>;
object.insert(std::move(*object.begin()));
object.begin() != object.end();
} && !requires(T &object) { object.resize(1); })
struct TypeSerializer<T> {
using item_type = std::remove_cvref_t<decltype(*std::declval<T>().begin())>;
static void serialize(Serializer &s, const T &t) {
s.serialize(static_cast<std::uint32_t>(t.size()));
for (auto &item : t) {
s.serialize(item);
}
}
static T deserialize(Deserializer &s) {
auto size = s.deserialize<std::uint32_t>();
if (s.failure()) {
return {};
}
T result;
for (std::uint32_t i = 0; i < size; ++i) {
result.insert(s.deserialize<item_type>());
if (s.failure()) {
return {};
}
}
return result;
}
};
namespace detail {
template <typename T> struct StructSerializerBuilder {
static constexpr std::array<StructSerializerField, rx::fieldCount<T>>
build() {
StructSerializerBuilder result;
auto impl = [&]<std::size_t... I>(std::index_sequence<I...>) {
static_cast<void>(T{FieldVisitor{&result, I}...});
};
impl(std::make_index_sequence<rx::fieldCount<T>>{});
std::size_t nextOffset = 0;
for (auto &field : result.fields) {
auto fieldOffset = alignUp(nextOffset, field.alignment);
nextOffset = fieldOffset + field.size;
field.offset = fieldOffset;
}
return result.fields;
}
private:
struct FieldVisitor {
StructSerializerBuilder *builder;
std::size_t fieldIndex;
template <typename FieldT> constexpr operator FieldT() {
builder->addField<FieldT>(fieldIndex);
return {};
}
};
template <typename FieldT> constexpr void addField(std::size_t index) {
fields[index] = StructSerializerField{
.offset = 0,
.alignment = alignof(FieldT),
.size = sizeof(FieldT),
.serialize =
+[](rx::Serializer &s, const void *object) {
s.serialize(*static_cast<const FieldT *>(object));
},
.deserialize =
+[](rx::Deserializer &s, void *object) {
s.deserialize(*static_cast<FieldT *>(object));
},
};
}
std::array<StructSerializerField, fieldCount<T>> fields;
};
} // namespace detail
template <typename T>
requires detail::SerializableClass<T> && (!detail::TriviallyRelocatable<T>)
struct TypeSerializer<T> {
static const auto &getFields() {
static const auto fields = detail::StructSerializerBuilder<T>::build();
return fields;
}
static void serialize(Serializer &s, const T &object) {
s.serialize<std::uint32_t>(sizeof(object));
auto bytes = std::bit_cast<std::byte *>(&object);
for (auto field : getFields()) {
field.serialize(s, bytes + field.offset);
}
}
static void deserialize(Deserializer &s, T &object) {
if (s.deserialize<std::uint32_t>() != sizeof(object)) {
s.setFailure();
return;
}
auto bytes = std::bit_cast<std::byte *>(&object);
for (auto field : getFields()) {
field.deserialize(s, bytes + field.offset);
if (s.failure()) {
return;
}
}
}
};
} // namespace rx