diff options
| author | Mica White <botahamec@outlook.com> | 2026-10-26 10:52:04 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-10-26 10:58:46 -0400 |
| commit | ec40f6e3acc7874b82709ab81b254c741529fdff (patch) | |
| tree | e3dfdc315bd0c6f1d2efcf442c2cfb3317d6810f /platforms/tortuise-script-wasm/src/storage.rs | |
| parent | e3c5839159903658a667f789963f20a31567b25c (diff) | |
Diffstat (limited to 'platforms/tortuise-script-wasm/src/storage.rs')
| -rw-r--r-- | platforms/tortuise-script-wasm/src/storage.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/platforms/tortuise-script-wasm/src/storage.rs b/platforms/tortuise-script-wasm/src/storage.rs new file mode 100644 index 0000000..2606e2b --- /dev/null +++ b/platforms/tortuise-script-wasm/src/storage.rs @@ -0,0 +1,194 @@ +use core::{ + alloc::Layout, + marker::PhantomData, + 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 storage_size(context: u64) -> usize; + pub unsafe fn load_storage(context: u64, ptr: *mut u8); + pub unsafe fn save_storage(context: u64, ptr: *const u8, size: usize); + pub safe fn is_saving_storage(context: u64) -> usize; + pub safe fn saving_storage_failed(context: u64) -> usize; + pub safe fn config_size(context: u64) -> usize; + pub unsafe fn load_config(context: u64, ptr: *mut u8); + pub unsafe fn save_config(context: u64, ptr: *const u8, size: usize); + pub safe fn is_saving_config(context: u64) -> usize; + pub safe fn saving_config_failed(context: u64) -> usize; + } +} + +fn load_storage<T: DeserializeOwned>(context: u64) -> Option<T> { + unsafe { + let size = tortuise::storage_size(context); + let layout = Layout::array::<u8>(size).unwrap(); + if layout.size() == 0 { + return None; + } + let ptr = alloc(layout); + tortuise::load_storage(context, ptr); + let data = core::slice::from_raw_parts(ptr, size); + let data = messagepack_serde::from_slice(data).ok()?; + dealloc(ptr, layout); + Some(data) + } +} + +fn save_storage<T: Serialize>(context: u64, state: &T) { + unsafe { + let data = messagepack_serde::to_vec(state).unwrap(); + tortuise::save_storage(context, data.as_ptr(), data.len()); + } +} + +fn load_config<T: DeserializeOwned>(context: u64) -> Option<T> { + unsafe { + let size = tortuise::config_size(context); + let layout = Layout::array::<u8>(size).unwrap(); + if layout.size() == 0 { + return None; + } + let ptr = alloc(layout); + tortuise::load_config(context, ptr); + let data = core::slice::from_raw_parts(ptr, size); + let data = toml::from_slice(data).ok()?; + dealloc(ptr, layout); + Some(data) + } +} + +fn save_config<T: Serialize>(context: u64, state: &T) { + unsafe { + let data = toml::to_string(state).unwrap(); + tortuise::save_config(context, data.as_ptr(), data.len()); + } +} + +pub struct Storage<T> { + initial_state: fn() -> T, +} + +pub struct Config<T> { + initial_state: fn() -> T, +} + +pub struct StorageMut<'a, 'cx, T: Serialize + Eq> { + context: u64, + original: T, + data: T, + _phantom: PhantomData<(&'a (), &'cx ())>, +} + +pub struct ConfigMut<'a, 'cx, T: Serialize + Eq> { + context: u64, + original: T, + data: T, + _phantom: PhantomData<(&'a (), &'cx ())>, +} + +impl<T: Serialize + DeserializeOwned + Clone + Eq> Storage<T> { + pub const fn new(initial_state: fn() -> T) -> Self { + Self { initial_state } + } + + pub fn load(&self, context: &ScriptContext) -> T { + load_storage(context.0).unwrap_or_else(self.initial_state) + } + + pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> { + let data = load_storage(context.0).unwrap_or_else(self.initial_state); + StorageMut { + context: context.0, + original: data.clone(), + data, + _phantom: PhantomData, + } + } + + pub fn is_saving(&self, context: &ScriptContext) -> bool { + tortuise::is_saving_storage(context.0) > 0 + } + + pub fn save_failed(&self, context: &ScriptContext) -> bool { + tortuise::saving_storage_failed(context.0) > 0 + } +} + +impl<T: Serialize + DeserializeOwned + Clone + Eq> Config<T> { + pub const fn new(initial_state: fn() -> T) -> Self { + Self { initial_state } + } + + pub fn load(&self, context: &ScriptContext) -> T { + load_config(context.0).unwrap_or_else(self.initial_state) + } + + pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> { + let data = load_config(context.0).unwrap_or_else(self.initial_state); + StorageMut { + context: context.0, + original: data.clone(), + data, + _phantom: PhantomData, + } + } + + pub fn is_saving(&self, context: &ScriptContext) -> bool { + tortuise::is_saving_config(context.0) > 0 + } + + pub fn save_failed(&self, context: &ScriptContext) -> bool { + tortuise::saving_config_failed(context.0) > 0 + } +} + +impl<T: Serialize + Eq> Deref for StorageMut<'_, '_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl<T: Serialize + Eq> DerefMut for StorageMut<'_, '_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +impl<T: Serialize + Eq> Drop for StorageMut<'_, '_, T> { + fn drop(&mut self) { + if self.data != self.original { + save_storage(self.context, &self.data); + } + } +} + +impl<T: Serialize + Eq> Deref for ConfigMut<'_, '_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl<T: Serialize + Eq> DerefMut for ConfigMut<'_, '_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +impl<T: Serialize + Eq> Drop for ConfigMut<'_, '_, T> { + fn drop(&mut self) { + if self.data != self.original { + save_config(self.context, &self.data); + } + } +} |
