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<T: DeserializeOwned>(&self) -> Option<T> {
unsafe {
let size = tortuise::state_size(self.0);
let layout = Layout::array::<u8>(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<T: Serialize>(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<T: Serialize> {
context: u64,
data: T,
}
impl<T: Serialize + DeserializeOwned> StateMut<T> {
pub fn load(context: &ScriptContext) -> Option<Self> {
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<T: Serialize> Deref for StateMut<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: Serialize> DerefMut for StateMut<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T: Serialize> Drop for StateMut<T> {
fn drop(&mut self) {
save_state(self.context, &self.data);
}
}
|