use core::{ alloc::Layout, ops::{Deref, DerefMut}, }; use alloc::alloc::{alloc, dealloc}; use serde::{Serialize, de::DeserializeOwned}; use crate::ScriptContext; mod tortuise { #[link(wasm_import_module = "tortuise")] unsafe extern "C" { pub safe fn state_size(context: u64) -> usize; pub unsafe fn load_state(context: u64, offset: *mut u8); pub unsafe fn save_state(context: u64, offset: *const u8, size: usize); } } impl ScriptContext { pub fn load_state(&self) -> Option { unsafe { let size = tortuise::state_size(self.0); let layout = Layout::array::(size).unwrap(); let ptr = alloc(layout); tortuise::load_state(self.0, ptr); let data = core::slice::from_raw_parts(ptr, size); let data = messagepack_serde::from_reader(data).ok()?; dealloc(ptr, layout); Some(data) } } } fn save_state(context: u64, state: &T) { unsafe { let data = messagepack_serde::to_vec(state).unwrap(); tortuise::save_state(context, data.as_ptr(), data.len()); } } pub struct StateMut { context: u64, data: T, } impl StateMut { pub fn load(context: &ScriptContext) -> Option { Some(Self { context: context.0, data: context.load_state()?, }) } pub fn load_or(context: &ScriptContext, default: T) -> Self { Self { context: context.0, data: context.load_state().unwrap_or(default), } } pub fn load_or_else(context: &ScriptContext, default: impl FnOnce() -> T) -> Self { Self { context: context.0, data: context.load_state().unwrap_or_else(default), } } pub fn load_or_default(context: &ScriptContext) -> Self where T: Default, { Self { context: context.0, data: context.load_state().unwrap_or_default(), } } } impl Deref for StateMut { type Target = T; fn deref(&self) -> &Self::Target { &self.data } } impl DerefMut for StateMut { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.data } } impl Drop for StateMut { fn drop(&mut self) { save_state(self.context, &self.data); } }