mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-09 00:11:24 +12:00
Savestates: HLE state saving POC in sys_lwmutex
This commit is contained in:
parent
2815aecd0c
commit
b692108f1e
12 changed files with 159 additions and 63 deletions
|
@ -39,6 +39,8 @@ public:
|
|||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
enum CellNotAnError : s32
|
||||
|
|
|
@ -69,10 +69,7 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
|||
// call the syscall
|
||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 1))
|
||||
{
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
lwmutex->all_info--;
|
||||
|
||||
|
@ -108,10 +105,7 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
|||
// call the syscall
|
||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 3))
|
||||
{
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
||||
{
|
||||
|
@ -158,10 +152,7 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
|||
return res;
|
||||
}
|
||||
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
lwmutex->all_info += +res;
|
||||
return CELL_OK;
|
||||
|
@ -183,10 +174,7 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
|||
// if locking succeeded, call the syscall
|
||||
error_code res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1);
|
||||
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
if (res > 0)
|
||||
{
|
||||
|
@ -225,10 +213,7 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
|
|||
// call the syscall
|
||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1))
|
||||
{
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
lwmutex->all_info--;
|
||||
|
||||
|
@ -261,10 +246,7 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
|
|||
// call the syscall
|
||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3))
|
||||
{
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
||||
{
|
||||
|
@ -290,6 +272,9 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
|||
return sys_cond_wait(ppu, lwcond->lwcond_queue, timeout);
|
||||
}
|
||||
|
||||
auto& sstate = *ppu.optional_savestate_state;
|
||||
const auto lwcond_ec = sstate.try_read<error_code>().second;
|
||||
|
||||
const be_t<u32> tid(ppu.id);
|
||||
|
||||
const vm::ptr<sys_lwmutex_t> lwmutex = lwcond->lwmutex;
|
||||
|
@ -301,18 +286,22 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
|||
}
|
||||
|
||||
// save old recursive value
|
||||
const be_t<u32> recursive_value = lwmutex->recursive_count;
|
||||
const be_t<u32> recursive_value = !lwcond_ec ? lwmutex->recursive_count : sstate.operator be_t<u32>();
|
||||
|
||||
// set special value
|
||||
lwmutex->vars.owner = lwmutex_reserved;
|
||||
lwmutex->recursive_count = 0;
|
||||
|
||||
// call the syscall
|
||||
const error_code res = _sys_lwcond_queue_wait(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, timeout);
|
||||
const error_code res = !lwcond_ec ? _sys_lwcond_queue_wait(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, timeout) : lwcond_ec;
|
||||
|
||||
if (ppu.test_stopped())
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
return 0;
|
||||
sstate.pos = 0;
|
||||
sstate(error_code{}, recursive_value); // Not aborted on mutex sleep
|
||||
return {};
|
||||
}
|
||||
|
||||
if (res == CELL_OK || res + 0u == CELL_ESRCH)
|
||||
|
@ -341,6 +330,13 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
|||
return res2;
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
sstate.pos = 0;
|
||||
sstate(res, recursive_value);
|
||||
return {};
|
||||
}
|
||||
|
||||
// if successfully locked, restore recursive value
|
||||
lwmutex->recursive_count = recursive_value;
|
||||
|
||||
|
|
|
@ -99,6 +99,15 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
|||
return sys_mutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||
}
|
||||
|
||||
auto& sstate = *ppu.optional_savestate_state;
|
||||
const bool aborted = sstate.try_read<bool>().second;
|
||||
|
||||
if (aborted)
|
||||
{
|
||||
// Restore timeout (SYS_SYNC_RETRY mode)
|
||||
sstate(timeout);
|
||||
}
|
||||
|
||||
const be_t<u32> tid(ppu.id);
|
||||
|
||||
// try to lock lightweight mutex
|
||||
|
@ -154,7 +163,10 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
|||
}
|
||||
|
||||
// atomically increment waiter value using 64 bit op
|
||||
lwmutex->all_info++;
|
||||
if (!aborted)
|
||||
{
|
||||
lwmutex->all_info++;
|
||||
}
|
||||
|
||||
if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
|
||||
{
|
||||
|
@ -167,9 +179,13 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
|||
// lock using the syscall
|
||||
const error_code res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||
|
||||
if (ppu.test_stopped())
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
return 0;
|
||||
sstate.pos = 0;
|
||||
sstate(true, timeout); // Aborted
|
||||
return {};
|
||||
}
|
||||
|
||||
lwmutex->all_info--;
|
||||
|
@ -216,9 +232,13 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
|||
|
||||
const error_code res_ = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||
|
||||
if (ppu.test_stopped())
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
return 0;
|
||||
sstate.pos = 0;
|
||||
sstate(true, timeout); // Aborted
|
||||
return {};
|
||||
}
|
||||
|
||||
if (res_ == CELL_OK)
|
||||
|
|
|
@ -24,7 +24,8 @@ error_code sys_spinlock_lock(ppu_thread& ppu, vm::ptr<atomic_be_t<u32>> lock)
|
|||
{
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
return 0;
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1396,7 +1396,7 @@ void ppu_thread::cpu_task()
|
|||
thread_ctrl::wait_on<atomic_wait::op_ne>(g_progr_ptotal, 0);
|
||||
g_fxo->get<progress_dialog_workaround>().skip_the_progress_dialog = true;
|
||||
|
||||
// Sadly we can't postpone initializing guest time because we need ti run PPU threads
|
||||
// Sadly we can't postpone initializing guest time because we need to run PPU threads
|
||||
// (the farther it's postponed, the less accuracy of guest time has been lost)
|
||||
Emu.FixGuestTime();
|
||||
|
||||
|
@ -1527,6 +1527,8 @@ ppu_thread::ppu_thread(const ppu_thread_params& param, std::string_view name, u3
|
|||
gpr[4] = param.arg1;
|
||||
}
|
||||
|
||||
optional_savestate_state = std::make_shared<utils::serial>();
|
||||
|
||||
// Trigger the scheduler
|
||||
state += cpu_flag::suspend;
|
||||
|
||||
|
@ -1575,10 +1577,15 @@ bool ppu_thread::savable() const
|
|||
|
||||
void ppu_thread::serialize_common(utils::serial& ar)
|
||||
{
|
||||
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio, optional_syscall_state);
|
||||
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio, optional_savestate_state);
|
||||
|
||||
for (v128& reg : vr)
|
||||
ar(reg._bytes);
|
||||
|
||||
if (optional_savestate_state->data.empty())
|
||||
{
|
||||
optional_savestate_state->clear();
|
||||
}
|
||||
}
|
||||
|
||||
ppu_thread::ppu_thread(utils::serial& ar)
|
||||
|
@ -1669,8 +1676,12 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
|||
|
||||
+[](ppu_thread& ppu) -> bool
|
||||
{
|
||||
const u32 op = vm::read32(ppu.cia);
|
||||
const auto& table = g_fxo->get<ppu_interpreter_rt>();
|
||||
ppu.loaded_from_savestate = true;
|
||||
ppu_execute_syscall(ppu, ppu.gpr[11]);
|
||||
table.decode(op)(ppu, {op}, vm::_ptr<u32>(ppu.cia), &ppu_ret);
|
||||
|
||||
ppu.optional_savestate_state->clear(); // Reset to writing state
|
||||
ppu.loaded_from_savestate = false;
|
||||
return true;
|
||||
}
|
||||
|
@ -1708,6 +1719,8 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
|||
|
||||
void ppu_thread::save(utils::serial& ar)
|
||||
{
|
||||
USING_SERIALIZATION_VERSION(ppu);
|
||||
|
||||
const u64 entry = std::bit_cast<u64>(entry_func);
|
||||
|
||||
ppu_join_status _joiner = joiner;
|
||||
|
@ -1870,7 +1883,7 @@ void ppu_thread::fast_call(u32 addr, u64 rtoc)
|
|||
}
|
||||
else if (old_cia)
|
||||
{
|
||||
if (state & cpu_flag::exit)
|
||||
if (state & cpu_flag::again)
|
||||
{
|
||||
ppu_log.error("HLE callstack savestate is not implemented!");
|
||||
}
|
||||
|
|
|
@ -318,9 +318,9 @@ public:
|
|||
} thread_name{ this };
|
||||
|
||||
// For savestates
|
||||
bool stop_flag_removal_protection = false; // If set, Emulator::Run won't remove stop flag
|
||||
bool stop_flag_removal_protection = false; // If set, Emulator::Run won't remove stop flag
|
||||
bool loaded_from_savestate = false; // Indicates the thread had just started straight from savestate load
|
||||
u64 optional_syscall_state{};
|
||||
std::shared_ptr<utils::serial> optional_savestate_state;
|
||||
bool interrupt_thread_executing = false;
|
||||
|
||||
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
|
||||
|
|
|
@ -285,6 +285,8 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
|||
// Further function result
|
||||
ppu.gpr[3] = CELL_OK;
|
||||
|
||||
auto& sstate = *ppu.optional_savestate_state;
|
||||
|
||||
const auto cond = idm::get<lv2_obj, lv2_cond>(cond_id, [&](lv2_cond& cond) -> s64
|
||||
{
|
||||
if (!ppu.loaded_from_savestate && cond.mutex->owner >> 1 != ppu.id)
|
||||
|
@ -294,7 +296,10 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
|||
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
if (ppu.loaded_from_savestate && ppu.optional_syscall_state & 1)
|
||||
const u64 syscall_state = sstate.try_read<u64>().second;
|
||||
sstate.clear();
|
||||
|
||||
if (syscall_state & 1)
|
||||
{
|
||||
// Mutex sleep
|
||||
ensure(!cond.mutex->try_own(ppu, ppu.id));
|
||||
|
@ -309,7 +314,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
|||
if (ppu.loaded_from_savestate)
|
||||
{
|
||||
cond.sleep(ppu, timeout);
|
||||
return static_cast<u32>(ppu.optional_syscall_state >> 32);
|
||||
return static_cast<u32>(syscall_state >> 32);
|
||||
}
|
||||
|
||||
// Unlock the mutex
|
||||
|
@ -356,7 +361,9 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
|||
break;
|
||||
}
|
||||
|
||||
ppu.optional_syscall_state = u32{mutex_sleep} | (u64{static_cast<u32>(cond.ret)} << 32);
|
||||
const u64 optional_syscall_state = u32{mutex_sleep} | (u64{static_cast<u32>(cond.ret)} << 32);
|
||||
sstate(optional_syscall_state);
|
||||
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -321,6 +321,8 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
|||
|
||||
std::shared_ptr<lv2_lwmutex> mutex;
|
||||
|
||||
auto& sstate = *ppu.optional_savestate_state;
|
||||
|
||||
const auto cond = idm::get<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond)
|
||||
{
|
||||
mutex = idm::get_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);
|
||||
|
@ -335,7 +337,10 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
|||
|
||||
std::lock_guard lock(cond.mutex);
|
||||
|
||||
if (ppu.loaded_from_savestate && ppu.optional_syscall_state)
|
||||
const bool mutex_sleep = sstate.try_read<bool>().second;
|
||||
sstate.clear();
|
||||
|
||||
if (mutex_sleep)
|
||||
{
|
||||
// Special: loading state from the point of waiting on lwmutex sleep queue
|
||||
std::lock_guard lock2(mutex->mutex);
|
||||
|
@ -403,7 +408,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
|||
break;
|
||||
}
|
||||
|
||||
ppu.optional_syscall_state = +mutex_sleep;
|
||||
sstate(mutex_sleep);
|
||||
ppu.state += cpu_flag::again;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,19 @@ namespace id_manager
|
|||
thread_local u32 g_id = 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool serialize<std::shared_ptr<utils::serial>>(utils::serial& ar, std::shared_ptr<utils::serial>& o)
|
||||
{
|
||||
if (!o || !ar.is_writing())
|
||||
{
|
||||
o.reset();
|
||||
o = std::make_shared<utils::serial>();
|
||||
}
|
||||
|
||||
ar(o->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<u128, id_manager::typeinfo>>& id_manager::get_typeinfo_map()
|
||||
{
|
||||
// Magic static
|
||||
|
|
|
@ -1569,7 +1569,7 @@ namespace vm
|
|||
if (!(ar.data.back() & page_writable) && is_memory_read_only_of_executable(addr))
|
||||
{
|
||||
// Revert changes
|
||||
ar.data.resize(ar.data.size() - (sizeof(u32) * 2 + sizeof(memory_page)));
|
||||
ar.data.resize(ar.seek_end(sizeof(u32) * 2 + sizeof(memory_page)));
|
||||
vm_log.success("Removed read-only memory block of the executable from savestate. (addr=0x%x, size=0x%x)", addr, shm.first);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ struct serial_ver_t
|
|||
std::set<u32> compatible_versions;
|
||||
};
|
||||
|
||||
static std::array<serial_ver_t, 18> s_serial_versions;
|
||||
static std::array<serial_ver_t, 22> s_serial_versions;
|
||||
|
||||
#define SERIALIZATION_VER(name, identifier, ...) \
|
||||
\
|
||||
|
@ -88,10 +88,10 @@ static std::array<serial_ver_t, 18> s_serial_versions;
|
|||
return ::s_serial_versions[identifier].current_version;\
|
||||
}
|
||||
|
||||
SERIALIZATION_VER(global_version, 0, 10) // For stuff not listed here
|
||||
SERIALIZATION_VER(global_version, 0, 11) // For stuff not listed here
|
||||
SERIALIZATION_VER(ppu, 1, 1)
|
||||
SERIALIZATION_VER(spu, 2, 1, 2)
|
||||
SERIALIZATION_VER(lv2_sync, 3, 1)
|
||||
SERIALIZATION_VER(lv2_sync, 3, 1)
|
||||
SERIALIZATION_VER(lv2_vm, 4, 1)
|
||||
SERIALIZATION_VER(lv2_net, 5, 1)
|
||||
SERIALIZATION_VER(lv2_fs, 6, 1)
|
||||
|
@ -121,7 +121,10 @@ SERIALIZATION_VER(cellCamera, 14, 1)
|
|||
SERIALIZATION_VER(cellGem, 15, 1)
|
||||
SERIALIZATION_VER(sceNpTrophy, 16, 1)
|
||||
SERIALIZATION_VER(cellMusic, 17, 1)
|
||||
SERIALIZATION_VER(cellVoice, 15, 1)
|
||||
SERIALIZATION_VER(cellVoice, 18, 1)
|
||||
SERIALIZATION_VER(cellGcm, 19, 1)
|
||||
SERIALIZATION_VER(sysPrxForUser, 20, 1)
|
||||
SERIALIZATION_VER(cellSaveData, 21, 1)
|
||||
|
||||
#undef SERIALIZATION_VER
|
||||
|
||||
|
@ -857,12 +860,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
|
|||
nse_t<u64, 1> offset;
|
||||
};
|
||||
|
||||
if (m_ar->data.size() <= sizeof(file_header))
|
||||
{
|
||||
return game_boot_result::savestate_corrupted;
|
||||
}
|
||||
|
||||
const file_header header = m_ar->operator file_header();
|
||||
const auto header = m_ar->try_read<file_header>().second;
|
||||
|
||||
if (header.magic != "RPCS3SAV"_u64)
|
||||
{
|
||||
|
@ -2376,6 +2374,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
|
|||
ar(usz{}); // Reserve memory to be patched later with correct size
|
||||
const usz old_size = ar.data.size();
|
||||
ar.data = tar_object::save_directory(path, std::move(ar.data));
|
||||
ar.seek_end();
|
||||
const usz tar_size = ar.data.size() - old_size;
|
||||
std::memcpy(ar.data.data() + old_size - sizeof(usz), &tar_size, sizeof(usz));
|
||||
sys_log.success("Saved the contents of directory '%s' (size=0x%x)", path, tar_size);
|
||||
|
@ -2511,7 +2510,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
|
|||
}
|
||||
|
||||
auto& ar = *m_ar;
|
||||
const usz pos = ar.data.size();
|
||||
const usz pos = ar.seek_end();
|
||||
std::memcpy(&ar.data[10], &pos, 8);// Set offset
|
||||
ar(used_serial);
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace utils
|
|||
struct serial
|
||||
{
|
||||
std::vector<u8> data;
|
||||
usz pos = umax;
|
||||
usz pos = 0;
|
||||
bool m_is_writing = true;
|
||||
|
||||
serial() = default;
|
||||
serial(const serial&) = delete;
|
||||
|
@ -44,7 +45,7 @@ namespace utils
|
|||
// Checks if this instance is currently used for serialization
|
||||
bool is_writing() const
|
||||
{
|
||||
return pos == umax;
|
||||
return m_is_writing;
|
||||
}
|
||||
|
||||
// Reserve memory for serialization
|
||||
|
@ -61,7 +62,8 @@ namespace utils
|
|||
{
|
||||
if (is_writing())
|
||||
{
|
||||
data.insert(data.end(), static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
|
||||
data.insert(data.begin() + pos, static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
|
||||
pos += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -287,7 +289,7 @@ namespace utils
|
|||
, serialize(const_cast<std::remove_cvref_t<Args>&>(static_cast<const Args&>(args)))), ...);
|
||||
}
|
||||
|
||||
// Convert serialization manager to deserializion manager (can't go the other way)
|
||||
// Convert serialization manager to deserializion manager
|
||||
// If no arg is provided reuse saved buffer
|
||||
void set_reading_state(std::vector<u8>&& _data = std::vector<u8>{})
|
||||
{
|
||||
|
@ -296,9 +298,25 @@ namespace utils
|
|||
data = std::move(_data);
|
||||
}
|
||||
|
||||
m_is_writing = false;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
// Reset to empty serialization manager
|
||||
void clear()
|
||||
{
|
||||
data.clear();
|
||||
m_is_writing = true;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
usz seek_end(usz backwards = 0)
|
||||
{
|
||||
ensure(pos >= backwards);
|
||||
pos = data.size() - backwards;
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename T> requires (std::is_copy_constructible_v<std::remove_const_t<T>>) && (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()
|
||||
|
@ -331,10 +349,32 @@ namespace utils
|
|||
}
|
||||
}
|
||||
|
||||
// Returns true if writable or readable and valid
|
||||
template <typename T> requires (std::is_copy_constructible_v<T> && std::is_constructible_v<T> && Bitcopy<T>)
|
||||
std::pair<bool, T> try_read()
|
||||
{
|
||||
if (is_writing())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const usz left = data.size() - pos;
|
||||
using type = std::remove_const_t<T>;
|
||||
|
||||
if (left >= sizeof(type))
|
||||
{
|
||||
u8 buf[sizeof(type)]{};
|
||||
ensure(raw_serialize(buf, sizeof(buf)));
|
||||
return {true, std::bit_cast<type>(buf)};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Returns true if valid, can be invalidated by setting pos to umax
|
||||
// Used when an invalid state is encountered somewhere in a place we can't check success code such as constructor)
|
||||
bool is_valid() const
|
||||
{
|
||||
return is_writing() || pos < data.size();
|
||||
return pos <= data.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue