Skip to main content

thruput_sdk/storage/
value.rs

1// src/storage/value.rs
2use super::keypath::*;
3use core::marker::PhantomData;
4pub use core::ops::{Deref, DerefMut};
5
6/// A typed storage slot for serialized values.
7///
8/// `Value<T>` is a handle to a storage location that holds a serialized `T`.
9/// All operations are immediate syscalls — no buffering.
10/// For in-place modification with auto-flush, use `load_mut()`.
11#[derive(Debug)]
12pub struct Value<T> {
13    key: LeafKey,
14    _marker: PhantomData<fn() -> T>,
15}
16
17impl<T> From<Keypath> for Value<T> {
18    fn from(keypath: Keypath) -> Self {
19        Self {
20            key: LeafKey::from(keypath),
21            _marker: PhantomData,
22        }
23    }
24}
25
26impl<T> super::sealed::Sealed for Value<T> {}
27impl<T> super::node::StorageType for Value<T> {}
28
29impl<T> Value<T> {
30    /// Return this value's storage key.
31    pub fn key(&self) -> &crate::types::StorageKey {
32        self.key.get()
33    }
34}
35
36impl<T> Value<T>
37where
38    T: for<'a> xdrs::ReadXDR<'a>,
39{
40    /// Deserialize and return the stored value.
41    /// Aborts if the key does not exist.
42    #[cfg_attr(not(target_arch = "riscv32"), track_caller)]
43    pub fn get(&self) -> T {
44        use crate::re_throw::OrReThrow;
45        self.try_get().or_re_throw()
46    }
47
48    /// Deserialize and return the stored value, reporting failure as `Err`
49    /// instead of aborting (the `try_` law; see the [`exn`](mod@crate::exn)
50    /// module docs).
51    ///
52    /// The `Err` is exactly the exception [`get`](Self::get) would have
53    /// thrown: a missing key is a `LibraryExn::Syscall` for which
54    /// [`is_storage_missing`](crate::exn::LibraryExn::is_storage_missing)
55    /// is true — use
56    /// [`none_if_missing`](crate::exn::NoneIfMissing::none_if_missing) to
57    /// treat just that case as an ordinary `None`. A value that fails to
58    /// decode as `T` (or exceeds the no-alloc read buffer — either way, the
59    /// slot was written by something other than `Value<T>::set`) is
60    /// `LibraryExn::StorageSerialization`; a value of another storage type
61    /// or any other syscall failure is a `LibraryExn::Syscall`.
62    pub fn try_get(&self) -> Result<T, crate::exn::LibraryExn> {
63        use crate::exn::{LibraryExn, syscall_exn};
64        use crate::sys::SyscallNo;
65        use crate::sys_result::bytestring::bytestring_read_into;
66
67        // Read into the reused in-place scratch and decode an owned value out
68        // of it before the borrow ends — no buffer of our own.
69        crate::call::with_scratch(|buf| {
70            match bytestring_read_into(self.key.get(), buf) {
71                Ok(Some(())) => {}
72                // Stored value exceeds the buffer: only possible if the slot
73                // was written by something other than Value<T>::set.
74                Ok(None) => return Err(LibraryExn::StorageSerialization),
75                Err(e) => {
76                    return Err(syscall_exn(SyscallNo::kBytestringGet, e));
77                }
78            }
79            xdrs::cxs::decode(buf.as_slice())
80                .map_err(|_| LibraryExn::StorageSerialization)
81        })
82    }
83}
84
85impl<T> Value<T>
86where
87    T: xdrs::WriteXDR,
88{
89    /// Serialize and store the value immediately.
90    pub fn set(&mut self, value: &T) {
91        use crate::re_throw::OrReThrow;
92        self.try_set(value).or_re_throw()
93    }
94
95    /// Like [`set`](Self::set), but report failure as `Err` instead of
96    /// aborting (the `try_` law; see the [`exn`](mod@crate::exn) module
97    /// docs). A value that fails to serialize is
98    /// `LibraryExn::StorageSerialization`; a syscall failure is
99    /// `LibraryExn::Syscall`. On `Err` the stored value is unmodified.
100    pub fn try_set(
101        &mut self,
102        value: &T,
103    ) -> Result<(), crate::exn::LibraryExn> {
104        self.try_set_impl(value, self.key.is_exclusive())
105    }
106
107    /// Serialize and store the value with an exclusive lock.
108    pub fn set_exclusive(&mut self, value: &T) {
109        use crate::re_throw::OrReThrow;
110        self.try_set_exclusive(value).or_re_throw()
111    }
112
113    /// Like [`set_exclusive`](Self::set_exclusive), but report failure as
114    /// `Err` instead of aborting (the `try_` law; see the
115    /// [`exn`](mod@crate::exn) module docs). The error contract is
116    /// [`try_set`](Self::try_set)'s. On `Err` the stored value is
117    /// unmodified and no lock is taken.
118    pub fn try_set_exclusive(
119        &mut self,
120        value: &T,
121    ) -> Result<(), crate::exn::LibraryExn> {
122        self.try_set_impl(value, true)
123    }
124
125    /// Shared serialize-and-set; `exclusive` selects the lock flavor.
126    fn try_set_impl(
127        &mut self,
128        value: &T,
129        exclusive: bool,
130    ) -> Result<(), crate::exn::LibraryExn> {
131        use crate::exn::LibraryExn;
132        use crate::re_throw::OrSyscallThrow;
133        use crate::sys::SyscallNo;
134        use crate::sys_result::bytestring::*;
135
136        crate::call::with_scratch(|buf| {
137            crate::internal::encode_scratch(buf, value)
138                .map_err(|_| LibraryExn::StorageSerialization)?;
139            let result = if exclusive {
140                bytestring_set_exclusive(&self.key, buf.as_slice())
141            } else {
142                bytestring_set(&self.key, buf.as_slice())
143            };
144            result.or_syscall_exn(SyscallNo::kBytestringSet)
145        })
146    }
147}
148
149impl<T> Value<T>
150where
151    T: xdrs::WriteXDR + for<'a> xdrs::ReadXDR<'a>,
152{
153    /// Load the value and return a mutable reference to it.
154    #[cfg_attr(not(target_arch = "riscv32"), track_caller)]
155    pub fn load_mut(&mut self) -> ValueMut<'_, T> {
156        let value = self.get();
157        ValueMut {
158            value: self,
159            data: value,
160            dirty: false,
161            _live: crate::storage::live_guards::LiveGuard::new(),
162        }
163    }
164}
165
166impl<T> super::flags::Flaggable for Value<T> {
167    fn storage_key(&self) -> &crate::types::StorageKey {
168        self.key.get()
169    }
170}
171
172impl<T> super::deletable::Deletable for Value<T> {
173    fn delete_internal(&mut self, exclusive: bool) {
174        let exclusive = exclusive || self.key.is_exclusive();
175        crate::sys_exn::delete::delete(&self.key, exclusive);
176    }
177}
178
179/// Mutable view into a `Value<T>`. Flushes `T` back to storage on drop if
180/// it was mutated (accessed through `DerefMut`) since the last
181/// [`flush`](Self::flush); a guard that was only read writes nothing.
182pub struct ValueMut<'a, T: __xdrs::WriteXDR> {
183    value: &'a mut Value<T>,
184    data: T,
185    dirty: bool,
186    /// Registers this guard as live for the terminal-return check. Holding
187    /// the field is the registration; on guest builds it is a `Drop`-less
188    /// ZST and compiles away. Any future deferred-write guard should carry
189    /// one too.
190    _live: crate::storage::live_guards::LiveGuard,
191}
192
193impl<T: __xdrs::WriteXDR> ValueMut<'_, T> {
194    /// Write the value back to storage now instead of waiting for drop.
195    ///
196    /// A guard that has not been mutated (no `DerefMut` access) since the
197    /// last flush is a no-op. After a flush the guard remains usable;
198    /// mutating it again re-arms the drop-time writeback.
199    pub fn flush(&mut self) {
200        use crate::re_throw::OrReThrow;
201        self.try_flush().or_re_throw()
202    }
203
204    /// Like [`flush`](Self::flush), but report failure as `Err` instead of
205    /// aborting (the `try_` law; see the [`exn`](mod@crate::exn) module
206    /// docs) — in particular, the only way to catch an error the drop-time
207    /// writeback would abort with. The `Err` is exactly
208    /// [`Value::try_set`]'s. On `Err` the guard still holds the value as
209    /// dirty: drop re-attempts the writeback and aborts if it still fails,
210    /// so a caught error cannot silently lose the write.
211    pub fn try_flush(&mut self) -> Result<(), crate::exn::LibraryExn> {
212        if !self.dirty {
213            return Ok(());
214        }
215        self.value.try_set(&self.data)?;
216        self.dirty = false;
217        Ok(())
218    }
219}
220
221impl<T: __xdrs::WriteXDR> Deref for ValueMut<'_, T> {
222    type Target = T;
223
224    fn deref(&self) -> &T {
225        &self.data
226    }
227}
228
229impl<T: __xdrs::WriteXDR> DerefMut for ValueMut<'_, T> {
230    fn deref_mut(&mut self) -> &mut T {
231        self.dirty = true;
232        &mut self.data
233    }
234}
235
236impl<T: __xdrs::WriteXDR> Drop for ValueMut<'_, T> {
237    fn drop(&mut self) {
238        if self.dirty {
239            self.value.set(&self.data);
240        }
241        // `_live` drops here, decrementing the count.
242    }
243}