1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-21 18:22:33 +01:00

Implement serialization.hpp, remove cereal submodule

Bump RSX capture version, use new serializer.
This commit is contained in:
Eladash 2021-06-01 19:13:05 +03:00 committed by Nekotekina
parent ddbe496097
commit 2169e8d935
16 changed files with 441 additions and 190 deletions

4
.gitmodules vendored
View File

@ -23,10 +23,6 @@
path = 3rdparty/SPIRV/SPIRV-Headers
url = ../../KhronosGroup/SPIRV-Headers.git
ignore = dirty
[submodule "3rdparty/cereal"]
path = 3rdparty/cereal
url = ../../RPCS3/cereal.git
ignore = dirty
[submodule "3rdparty/zlib"]
path = 3rdparty/zlib/zlib
url = ../../madler/zlib

View File

@ -103,12 +103,6 @@ else()
target_include_directories(xxhash INTERFACE xxHash)
endif()
# cereal
add_library(3rdparty_cereal INTERFACE)
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
# OpenGL
# Prefer GLVND for OpenGL rather than legacy, unless it's been defined elsewhere, in the case of AppImage builds
@ -351,7 +345,6 @@ add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
add_library(3rdparty::xxhash ALIAS xxhash)
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
add_library(3rdparty::discordRPC ALIAS 3rdparty_discordRPC)

1
3rdparty/cereal vendored

@ -1 +0,0 @@
Subproject commit 60c69df968d1c72c998cd5f23ba34e2e3718a84b

View File

@ -3,7 +3,7 @@
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<IncludePath>.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi;..\3rdparty\Optional;..\3rdparty\xxhash</IncludePath>
<IncludePath>.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi;..\3rdparty\Optional;..\3rdparty\xxhash</IncludePath>
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>

View File

@ -38,7 +38,6 @@ target_sources(rpcs3_emu PRIVATE
../util/atomic.cpp
../util/logs.cpp
../util/yaml.cpp
../util/cereal.cpp
../util/vm_native.cpp
../util/dyn_lib.cpp
../util/sysinfo.cpp
@ -76,12 +75,6 @@ else()
set_source_files_properties("../util/yaml.cpp" PROPERTIES COMPILE_FLAGS -fexceptions)
endif()
if(MSVC)
set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS /GR)
else()
set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS -frtti)
endif()
# Crypto
target_sources(rpcs3_emu PRIVATE
../Crypto/aes.cpp
@ -499,7 +492,7 @@ endif()
target_link_libraries(rpcs3_emu
PUBLIC
3rdparty::ffmpeg 3rdparty::cereal
3rdparty::ffmpeg
3rdparty::opengl 3rdparty::stblib
3rdparty::vulkan 3rdparty::glew
3rdparty::libusb 3rdparty::wolfssl

View File

@ -8,35 +8,27 @@
namespace rsx
{
constexpr u32 FRAME_CAPTURE_MAGIC = 0x52524300; // ascii 'RRC/0'
constexpr u32 FRAME_CAPTURE_VERSION = 0x4;
enum : u32
{
c_fc_magic = "RRC"_u32,
c_fc_version = 0x5,
};
struct frame_capture_data
{
struct memory_block_data
{
std::vector<u8> data{};
template<typename Archive>
void serialize(Archive& ar)
{
ar(data);
}
};
// simple block to hold ps3 address and data
struct memory_block
{
static constexpr bool enable_bitcopy = true;
u32 offset; // Offset in rsx address space
u32 location; // rsx memory location of the block
u64 data_state;
template<typename Archive>
void serialize(Archive & ar)
{
ar(offset);
ar(location);
ar(data_state);
}
};
struct replay_command
@ -45,101 +37,61 @@ namespace rsx
std::unordered_set<u64> memory_state{}; // index into memory_map for the various memory blocks that need applying before this command can run
u64 tile_state{0}; // tile state for this command
u64 display_buffer_state{0};
template<typename Archive>
void serialize(Archive & ar)
{
ar(rsx_command);
ar(memory_state);
ar(tile_state);
ar(display_buffer_state);
}
};
struct tile_info
{
static constexpr bool enable_bitcopy = true;
u32 tile;
u32 limit;
u32 pitch;
u32 format;
template<typename Archive>
void serialize(Archive & ar)
{
ar(tile);
ar(limit);
ar(pitch);
ar(format);
}
};
struct zcull_info
{
static constexpr bool enable_bitcopy = true;
u32 region;
u32 size;
u32 start;
u32 offset;
u32 status0;
u32 status1;
template<typename Archive>
void serialize(Archive & ar)
{
ar(region);
ar(size);
ar(start);
ar(offset);
ar(status0);
ar(status1);
}
};
// bleh, may need to break these out, might be unnecessary to do both always
struct tile_state
{
static constexpr bool enable_bitcopy = true;
tile_info tiles[15]{};
zcull_info zculls[8]{};
template<typename Archive>
void serialize(Archive & ar)
{
ar(tiles);
ar(zculls);
}
};
struct buffer_state
{
static constexpr bool enable_bitcopy = true;
u32 width{0};
u32 height{0};
u32 pitch{0};
u32 offset{0};
template<typename Archive>
void serialize(Archive & ar)
{
ar(width);
ar(height);
ar(pitch);
ar(offset);
}
};
struct display_buffers_state
{
static constexpr bool enable_bitcopy = true;
std::array<buffer_state, 8> buffers{};
u32 count{0};
template<typename Archive>
void serialize(Archive & ar)
{
ar(buffers);
ar(count);
}
};
u32 magic;
u32 version;
u32 magic = c_fc_magic;
u32 version = c_fc_version;
u32 LE_format = std::endian::little == std::endian::native;
// hashmap of holding various states for tile
std::unordered_map<u64, tile_state> tile_map;
// hashmap of various memory 'changes' that can be applied to ps3 memory
@ -153,23 +105,10 @@ namespace rsx
// Initial registers state at the beginning of the capture
rsx::rsx_state reg_state;
template<typename Archive>
void serialize(Archive & ar)
{
ar(magic);
ar(version);
ar(tile_map);
ar(memory_map);
ar(memory_data_map);
ar(display_buffers_map);
ar(replay_commands);
ar(reg_state);
}
void reset()
{
magic = FRAME_CAPTURE_MAGIC;
version = FRAME_CAPTURE_VERSION;
magic = c_fc_magic;
version = c_fc_version;
tile_map.clear();
memory_map.clear();
replay_commands.clear();

View File

@ -19,7 +19,7 @@
#include "Utilities/date_time.h"
#include "Utilities/StrUtil.h"
#include "util/cereal.hpp"
#include "util/serialization.hpp"
#include "util/asm.hpp"
#include <span>
@ -39,6 +39,37 @@ rsx::frame_capture_data frame_capture;
extern CellGcmOffsetTable offsetTable;
extern thread_local std::string(*g_tls_log_prefix)();
template <>
bool serialize<rsx::rsx_state>(utils::serial& ar, rsx::rsx_state& o)
{
return ar(o.transform_program, /*o.transform_constants,*/ o.registers);
}
template <>
bool serialize<rsx::frame_capture_data>(utils::serial& ar, rsx::frame_capture_data& o)
{
ar(o.magic, o.version, o.LE_format);
if (o.magic != rsx::c_fc_magic || o.version != rsx::c_fc_version || o.LE_format != (std::endian::little == std::endian::native))
{
return false;
}
return ar(o.tile_map, o.memory_map, o.memory_data_map, o.display_buffers_map, o.replay_commands, o.reg_state);
}
template <>
bool serialize<rsx::frame_capture_data::memory_block_data>(utils::serial& ar, rsx::frame_capture_data::memory_block_data& o)
{
return ar(o.data);
}
template <>
bool serialize<rsx::frame_capture_data::replay_command>(utils::serial& ar, rsx::frame_capture_data::replay_command& o)
{
return ar(o.rsx_command, o.memory_state, o.tile_state, o.display_buffer_state);
}
namespace rsx
{
std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;
@ -2861,11 +2892,14 @@ namespace rsx
const std::string file_path = fs::get_config_dir() + "captures/" + Emu.GetTitleID() + "_" + date_time::current_time_narrow() + "_capture.rrc";
// todo: may want to compress this data?
const std::string file_data = cereal_serialize(frame_capture);
utils::serial save_manager;
save_manager.reserve(0x800'0000); // 128MB
save_manager(frame_capture);
fs::pending_file temp(file_path);
if (temp.file && (temp.file.write(file_data), temp.commit(false)))
if (temp.file && (temp.file.write(save_manager.data), temp.commit(false)))
{
rsx_log.success("Capture successful: %s", file_path);
}

View File

@ -554,15 +554,6 @@ namespace rsx
void init();
template<typename Archive>
void serialize(Archive & ar)
{
ar(transform_program,
// transform_constants,
registers
);
}
u16 viewport_width() const
{
return decode<NV4097_SET_VIEWPORT_HORIZONTAL>().width();

View File

@ -30,7 +30,7 @@
#include "../Crypto/unself.h"
#include "util/yaml.hpp"
#include "util/logs.hpp"
#include "util/cereal.hpp"
#include "util/serialization.hpp"
#include <fstream>
#include <memory>
@ -380,18 +380,31 @@ bool Emulator::BootRsxCapture(const std::string& path)
}
std::unique_ptr<rsx::frame_capture_data> frame = std::make_unique<rsx::frame_capture_data>();
cereal_deserialize(*frame, in_file.to_string());
utils::serial load_manager;
load_manager.set_reading_state(in_file.to_vector<u8>());
load_manager(*frame);
in_file.close();
if (frame->magic != rsx::FRAME_CAPTURE_MAGIC)
if (frame->magic != rsx::c_fc_magic)
{
sys_log.error("Invalid rsx capture file!");
return false;
}
if (frame->version != rsx::FRAME_CAPTURE_VERSION)
if (frame->version != rsx::c_fc_version)
{
sys_log.error("Rsx capture file version not supported! Expected %d, found %d", rsx::FRAME_CAPTURE_VERSION, frame->version);
sys_log.error("Rsx capture file version not supported! Expected %d, found %d", +rsx::c_fc_version, frame->version);
return false;
}
if (frame->LE_format != (std::endian::little == std::endian::native))
{
static constexpr std::string_view machines[2]{"Big-Endian", "Little-Endian"};
sys_log.error("Rsx capture byte endianness not supported! Expected %s format, found %s format"
, machines[frame->LE_format ^ 1], machines[frame->LE_format]);
return false;
}

View File

@ -100,10 +100,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ExceptionHandling>Sync</ExceptionHandling>
</ClCompile>
<ClCompile Include="util\cereal.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
<ClCompile Include="..\Utilities\bin_patch.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@ -490,6 +486,7 @@
<ClInclude Include="Emu\system_config_types.h" />
<ClInclude Include="Loader\mself.hpp" />
<ClInclude Include="util\atomic.hpp" />
<ClInclude Include="util\serialization.hpp" />
<ClInclude Include="util\v128.hpp" />
<ClInclude Include="util\v128sse.hpp" />
<ClInclude Include="util\to_endian.hpp" />

View File

@ -843,9 +843,6 @@
<ClCompile Include="util\yaml.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="util\cereal.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="util\cpu_stats.cpp">
<Filter>Utilities</Filter>
</ClCompile>
@ -1962,6 +1959,9 @@
<ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClInclude>
<ClInclude Include="util\serialization.hpp">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">

View File

@ -62,12 +62,12 @@
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib</IncludePath>
<IncludePath>..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib</IncludePath>
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
<IncludePath>..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib</IncludePath>
<IncludePath>..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>

View File

@ -1,49 +0,0 @@
#include "util/cereal.hpp"
#include <string>
#include "Utilities/StrFmt.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Capture/rsx_capture.h"
#ifndef _MSC_VER
#pragma GCC diagnostic ignored "-Weffc++"
#endif
#include "cereal/archives/binary.hpp"
#include <cereal/types/vector.hpp>
#include <cereal/types/array.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/unordered_set.hpp>
#include <cereal/types/unordered_map.hpp>
#include <sstream>
namespace cereal
{
[[noreturn]] void throw_exception(const std::string& err)
{
fmt::throw_exception("%s", err);
}
[[noreturn]] void throw_exception(const char* err)
{
fmt::throw_exception("%s", err);
}
}
template <>
std::string cereal_serialize<rsx::frame_capture_data>(const rsx::frame_capture_data& data)
{
std::ostringstream os;
cereal::BinaryOutputArchive archive(os);
archive(data);
return os.str();
}
template <>
void cereal_deserialize<rsx::frame_capture_data>(rsx::frame_capture_data& data, const std::string& src)
{
std::istringstream is(src);
cereal::BinaryInputArchive archive(is);
archive(data);
}

View File

@ -1,9 +0,0 @@
#pragma once
#include <string>
template <typename T>
std::string cereal_serialize(const T&);
template <typename T>
void cereal_deserialize(T& data, const std::string& src);

View File

@ -0,0 +1,346 @@
#pragma once
#include "util/types.hpp"
#include <vector>
namespace stx
{
template <typename T>
struct exact_t
{
T obj;
exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
// TODO: More conversions
template <typename U> requires (std::is_same_v<U&, T>)
operator U&() const { return obj; };
};
}
namespace utils
{
template <typename T>
concept FastRandomAccess = requires (T& obj)
{
std::data(obj)[0];
};
template <typename T>
concept Reservable = requires (T& obj)
{
obj.reserve(0);
};
template <typename T>
concept Bitcopy = (std::is_arithmetic_v<T>) || (std::is_enum_v<T>) || requires (T& obj)
{
std::enable_if_t<static_cast<bool>(std::remove_cv_t<T>::enable_bitcopy)>();
};
template <typename T>
concept TupleAlike = requires ()
{
std::tuple_size<std::remove_cv_t<T>>::value;
};
template <typename T>
concept ListAlike = requires (T& obj) { obj.insert(obj.end(), std::declval<typename T::value_type>()); };
struct serial
{
std::vector<u8> data;
usz pos = umax;
// Checks if this strcuture is currently used for serialization
bool is_writing() const
{
return pos == umax;
}
// Reserve memory for serialization
void reserve(usz size)
{
// Is a NO-OP for deserialization in order to allow usage from serialization specializations regardless
if (is_writing())
{
data.reserve(data.size() + size);
}
}
bool raw_serialize(const void* ptr, usz size)
{
if (is_writing())
{
data.insert(data.end(), static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
return true;
}
ensure(data.size() - pos >= size);
std::memcpy(const_cast<void*>(ptr), data.data() + pos, size);
pos += size;
return true;
}
// (De)serialization function
template <typename T>
bool serialize(T& obj)
{
// Fallback to global overload
return ::serialize(*this, obj);
}
// Enabled for fundamental types, enumerations and if specfied explicitly that type can be saved in pure bitwise manner
template <typename T> requires Bitcopy<T>
bool serialize(T& obj)
{
return raw_serialize(std::addressof(obj), sizeof(obj));
}
// std::vector, std::basic_string
template <typename T> requires FastRandomAccess<T> && ListAlike<T>
bool serialize(T& obj)
{
if (is_writing())
{
for (usz i = obj.size();;)
{
const usz i_old = std::exchange(i, i >> 7);
operator()<u8>(static_cast<u8>(i_old % 0x80) | (u8{!!i} << 7));
if (!i)
break;
}
if constexpr (std::is_trivially_copyable_v<typename std::remove_reference_t<T>::value_type>)
{
raw_serialize(obj.data(), sizeof(obj[0]) * obj.size());
}
else
{
for (auto&& value : obj)
{
if (!serialize(value))
{
return false;
}
}
}
return true;
}
obj.clear();
usz size = 0;
for (u32 i = 0;; i += 7)
{
u8 byte_data = 0;
if (!raw_serialize(&byte_data, 1))
{
return false;
}
size |= static_cast<usz>(byte_data % 0x80) << i;
if (!(byte_data >> 7))
{
break;
}
}
obj.resize(size);
if constexpr (std::is_trivially_copyable_v<typename T::value_type>)
{
if (!raw_serialize(obj.data(), sizeof(obj[0]) * size))
{
obj.clear();
return false;
}
}
else
{
for (auto&& value : obj)
{
if (!serialize(value))
{
obj.clear();
return false;
}
}
}
return true;
}
// C-array, std::array, std::span
template <typename T> requires FastRandomAccess<T> && (!ListAlike<T>) && (!Bitcopy<T>)
bool serialize(T& obj)
{
if constexpr (std::is_trivially_copyable_v<std::remove_reference_t<decltype(std::declval<T>()[0])>>)
{
return raw_serialize(std::data(obj), sizeof(obj[0]) * std::size(obj));
}
else
{
for (auto&& value : obj)
{
if (!serialize(value))
{
return false;
}
}
return true;
}
}
// std::deque, std::list, std::(unordered_)set, std::(unordered_)map, std::(unordered_)multiset, std::(unordered_)multimap
template <typename T> requires (!FastRandomAccess<T>) && ListAlike<T>
bool serialize(T& obj)
{
if (is_writing())
{
for (usz i = obj.size();;)
{
const usz i_old = std::exchange(i, i >> 7);
operator()<u8>(static_cast<u8>(i_old % 0x80) | (u8{!!i} << 7));
if (!i)
break;
}
for (auto&& value : obj)
{
if (!serialize(value))
{
return false;
}
}
return true;
}
obj.clear();
usz size = 0;
for (u32 i = 0;; i += 7)
{
u8 byte_data = 0;
if (!raw_serialize(&byte_data, 1))
{
return false;
}
size |= static_cast<usz>(byte_data % 0x80) << i;
if (!(byte_data >> 7))
{
break;
}
}
if constexpr (Reservable<T>)
{
obj.reserve(size);
}
for (usz i = 0; i < size; i++)
{
obj.insert(obj.end(), static_cast<typename T::value_type>(*this));
if (!is_valid())
{
obj.clear();
return false;
}
}
return true;
}
template <usz i = 0, typename T>
bool serialize_tuple(T& obj)
{
const bool res = serialize(std::get<i>(obj));
constexpr usz next_i = std::min<usz>(i + 1, std::tuple_size_v<T> - 1);
if constexpr (next_i == i)
{
return res;
}
else
{
return res && serialize_tuple<next_i>(obj);
}
}
// std::pair, std::tuple
template <typename T> requires TupleAlike<T> && (!FastRandomAccess<T>)
bool serialize(T& obj)
{
return serialize_tuple(obj);
}
// Wrapper for serialize(T&), allows to pass multiple objects at once
template <typename... Args>
bool operator()(Args&&... args)
{
return ((AUDIT(!std::is_const_v<std::remove_reference_t<Args>> || is_writing())
, serialize(const_cast<Args&>(static_cast<const Args&>(args)))), ...);
}
// Convert serialization manager to deserializion manager (can't go the other way)
// If no arg provided reuse saved buffer
void set_reading_state(std::vector<u8>&& _data = std::vector<u8>{})
{
if (!_data.empty())
{
data = std::move(_data);
}
pos = 0;
}
template <typename T> requires (std::is_constructible_v<std::remove_const_t<T>> || Bitcopy<std::remove_const_t<T>> ||
std::is_constructible_v<std::remove_const_t<T>, stx::exact_t<serial&>> || TupleAlike<std::remove_const_t<T>>)
operator T()
{
AUDIT(!is_writing());
using type = std::remove_const_t<T>;
if constexpr (Bitcopy<T>)
{
u8 buf[sizeof(type)]{};
ensure(raw_serialize(buf, sizeof(buf)));
return std::bit_cast<type>(buf);
}
else if constexpr (std::is_constructible_v<type, stx::exact_t<serial&>>)
{
return type(stx::exact_t<serial&>(*this));
}
else if constexpr (std::is_constructible_v<type>)
{
type value{};
ensure(serialize(value));
return value;
}
else if constexpr (TupleAlike<T>)
{
static_assert(std::tuple_size_v<type> == 2, "Unimplemented tuple serialization!");
return type{ operator std::remove_cvref_t<decltype(std::get<0>(std::declval<type&>()))>
, operator std::remove_cvref_t<decltype(std::get<1>(std::declval<type&>()))> };
}
}
// Returns true if writable or readable and valid
bool is_valid() const
{
return is_writing() || pos < data.size();
}
};
}

View File

@ -1070,3 +1070,11 @@ constexpr bool is_same_ptr(const volatile Y* ptr)
template <typename X, typename Y>
concept PtrSame = (is_same_ptr<X, Y>());
namespace utils
{
struct serial;
}
template <typename T>
extern bool serialize(utils::serial& ar, T& obj);