Skip to main content

truce_core/
custom_state.rs

1//! Custom state serialization for plugin-specific persistent data.
2//!
3//! Use `#[derive(State)]` on a struct to auto-generate binary serialization:
4//!
5//! ```ignore
6//! #[derive(State, Default)]
7//! pub struct MyState {
8//!     pub instance_name: String,
9//!     pub view_mode: u8,
10//!     pub selected_ids: Vec<u32>,
11//! }
12//! ```
13//!
14//! Then use it in your plugin's `save_state`/`load_state`:
15//!
16//! ```ignore
17//! fn save_state(&self) -> Vec<u8> {
18//!     self.persistent.serialize()
19//! }
20//! fn load_state(&mut self, data: &[u8]) -> Result<(), StateLoadError> {
21//!     match MyState::deserialize(data) {
22//!         Some(s) => { self.persistent = s; Ok(()) }
23//!         None => Err(StateLoadError::Malformed("MyState")),
24//!     }
25//! }
26//! ```
27
28/// Re-exported so plugin authors name the lock-free `#[persist]` cell
29/// as `AtomicCell` through the prelude, without depending on crossbeam.
30pub use crossbeam_utils::atomic::AtomicCell;
31
32/// Cursor for reading binary state data.
33pub struct StateCursor<'a> {
34    data: &'a [u8],
35    pos: usize,
36}
37
38impl<'a> StateCursor<'a> {
39    #[must_use]
40    pub fn new(data: &'a [u8]) -> Self {
41        Self { data, pos: 0 }
42    }
43
44    #[must_use]
45    pub fn remaining(&self) -> usize {
46        self.data.len().saturating_sub(self.pos)
47    }
48
49    pub fn read_bytes(&mut self, n: usize) -> Option<&'a [u8]> {
50        if self.pos + n > self.data.len() {
51            return None;
52        }
53        let slice = &self.data[self.pos..self.pos + n];
54        self.pos += n;
55        Some(slice)
56    }
57
58    /// Skip the next field (reads its encoded size and advances past it).
59    /// Returns false if the data is malformed.
60    ///
61    /// # Panics
62    ///
63    /// Does not panic - the `expect` inside is unreachable because
64    /// `read_bytes(4)` only returns `Some` when the slice is exactly
65    /// 4 bytes long.
66    pub fn skip_field(&mut self) -> bool {
67        // Fields are prefixed with a u32 byte length by the derive macro.
68        if let Some(bytes) = self.read_bytes(4) {
69            // `read_bytes(4)` returns `Some(slice of length 4)` or
70            // `None`, so the `try_into::<[u8; 4]>()` here cannot fail.
71            // The `expect` documents that invariant for readers.
72            let len = u32::from_le_bytes(
73                bytes
74                    .try_into()
75                    .expect("read_bytes(4) returned a slice of unexpected length"),
76            ) as usize;
77            if self.pos + len <= self.data.len() {
78                self.pos += len;
79                return true;
80            }
81        }
82        false
83    }
84}
85
86/// Trait for types that can be serialized as a single state field.
87///
88/// Implemented for primitives, `String`, `Vec<T>`, and `Option<T>`.
89pub trait StateField: Sized {
90    fn write_field(&self, buf: &mut Vec<u8>);
91    fn read_field(cursor: &mut StateCursor) -> Option<Self>;
92}
93
94/// Trait for custom plugin state structs.
95///
96/// Derive with `#[derive(State)]`. The struct must also implement `Default`
97/// so missing fields can be filled with defaults when loading old state.
98pub trait State: Sized + Default {
99    /// Serialize into `buf`. Clears `buf` first, then reuses its
100    /// capacity - calling this repeatedly with the same buffer is
101    /// allocation-free once warmed, so it is the form to use on the
102    /// audio thread (e.g. `PluginLogic::snapshot_into`).
103    fn serialize_into(&self, buf: &mut Vec<u8>);
104
105    /// Serialize to a fresh `Vec`. Convenience wrapper over
106    /// [`Self::serialize_into`]; allocates, so prefer `serialize_into`
107    /// on the real-time path.
108    #[must_use]
109    fn serialize(&self) -> Vec<u8> {
110        let mut buf = Vec::new();
111        self.serialize_into(&mut buf);
112        buf
113    }
114
115    fn deserialize(data: &[u8]) -> Option<Self>;
116}
117
118/// A `#[persist]` field on a `#[derive(Params)]` struct: an
119/// interior-mutable, `Sync` wrapper around a [`StateField`] value that
120/// the host saves alongside the parameter values (session / preset) and
121/// restores on load. Implemented for `RwLock<T>` / `Mutex<T>` where
122/// `T: StateField` (a primitive, `String`, `Vec`, `Option`, or a
123/// `#[derive(State)]` struct - the derive also emits a `StateField`
124/// impl). Interior mutability is required because a load reaches the
125/// field through `&Params` (the store is shared via `Arc<Params>`).
126pub trait PersistField {
127    /// Append the current value's bytes.
128    fn persist_write(&self, buf: &mut Vec<u8>);
129    /// Read a value from `cursor` and store it in place. A short or
130    /// malformed read leaves the current value untouched.
131    fn persist_read(&self, cursor: &mut StateCursor);
132}
133
134impl<T: StateField> PersistField for std::sync::RwLock<T> {
135    fn persist_write(&self, buf: &mut Vec<u8>) {
136        if let Ok(guard) = self.read() {
137            guard.write_field(buf);
138        }
139    }
140    fn persist_read(&self, cursor: &mut StateCursor) {
141        if let Some(value) = T::read_field(cursor)
142            && let Ok(mut guard) = self.write()
143        {
144            *guard = value;
145        }
146    }
147}
148
149impl<T: StateField> PersistField for std::sync::Mutex<T> {
150    fn persist_write(&self, buf: &mut Vec<u8>) {
151        if let Ok(guard) = self.lock() {
152            guard.write_field(buf);
153        }
154    }
155    fn persist_read(&self, cursor: &mut StateCursor) {
156        if let Some(value) = T::read_field(cursor)
157            && let Ok(mut guard) = self.lock()
158        {
159            *guard = value;
160        }
161    }
162}
163
164/// Lock-free-friendly cell for `Copy` config: a persisted `f32`, small
165/// enum, or index reads as `AtomicCell<T>` instead of sitting behind a
166/// `Mutex`. `AtomicCell` is genuinely atomic for word-sized types and
167/// falls back to an internal lock for larger ones.
168impl<T: StateField + Copy> PersistField for AtomicCell<T> {
169    fn persist_write(&self, buf: &mut Vec<u8>) {
170        self.load().write_field(buf);
171    }
172    fn persist_read(&self, cursor: &mut StateCursor) {
173        if let Some(value) = T::read_field(cursor) {
174            self.store(value);
175        }
176    }
177}
178
179// ---------------------------------------------------------------------------
180// StateField implementations for primitives
181// ---------------------------------------------------------------------------
182
183macro_rules! impl_state_field_int {
184    ($($ty:ty),*) => {
185        $(
186            impl StateField for $ty {
187                fn write_field(&self, buf: &mut Vec<u8>) {
188                    buf.extend_from_slice(&self.to_le_bytes());
189                }
190                fn read_field(cursor: &mut StateCursor) -> Option<Self> {
191                    let bytes = cursor.read_bytes(std::mem::size_of::<Self>())?;
192                    Some(Self::from_le_bytes(bytes.try_into().ok()?))
193                }
194            }
195        )*
196    };
197}
198
199impl_state_field_int!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
200
201impl StateField for bool {
202    fn write_field(&self, buf: &mut Vec<u8>) {
203        buf.push(u8::from(*self));
204    }
205    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
206        let b = cursor.read_bytes(1)?;
207        Some(b[0] != 0)
208    }
209}
210
211impl StateField for String {
212    fn write_field(&self, buf: &mut Vec<u8>) {
213        let bytes = self.as_bytes();
214        crate::cast::len_u32(bytes.len()).write_field(buf);
215        buf.extend_from_slice(bytes);
216    }
217    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
218        let len = u32::read_field(cursor)? as usize;
219        let bytes = cursor.read_bytes(len)?;
220        String::from_utf8(bytes.to_vec()).ok()
221    }
222}
223
224impl<T: StateField> StateField for Vec<T> {
225    fn write_field(&self, buf: &mut Vec<u8>) {
226        crate::cast::len_u32(self.len()).write_field(buf);
227        for item in self {
228            item.write_field(buf);
229        }
230    }
231    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
232        let len = u32::read_field(cursor)? as usize;
233        let mut vec = Vec::with_capacity(len.min(1024));
234        for _ in 0..len {
235            vec.push(T::read_field(cursor)?);
236        }
237        Some(vec)
238    }
239}
240
241impl<T: StateField> StateField for Option<T> {
242    fn write_field(&self, buf: &mut Vec<u8>) {
243        match self {
244            Some(val) => {
245                1u8.write_field(buf);
246                val.write_field(buf);
247            }
248            None => {
249                0u8.write_field(buf);
250            }
251        }
252    }
253    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
254        let tag = u8::read_field(cursor)?;
255        if tag == 0 {
256            Some(None)
257        } else {
258            Some(Some(T::read_field(cursor)?))
259        }
260    }
261}
262
263// ---------------------------------------------------------------------------
264// StateBinding - typed wrapper for editor state access
265// ---------------------------------------------------------------------------
266
267use crate::editor::PluginContext;
268use std::sync::Arc;
269
270/// Typed state binding for editors.
271///
272/// Wraps the `get_state`/`set_state` closures from `PluginContext` with
273/// typed serialization. Caches the deserialized state to avoid repeated
274/// deserialization each frame.
275///
276/// ```ignore
277/// struct MyEditor {
278///     state: StateBinding<PersistentState>,
279/// }
280///
281/// // In open():
282/// self.state = StateBinding::new(&context);
283///
284/// // In state_changed():
285/// self.state.sync();
286///
287/// // Reading:
288/// let name = &self.state.get().instance_name;
289///
290/// // Writing:
291/// self.state.update(|s| s.instance_name = new_name);
292/// ```
293pub struct StateBinding<T: State> {
294    cached: T,
295    get_state: Arc<dyn Fn() -> Vec<u8> + Send + Sync>,
296    set_state: Arc<dyn Fn(Vec<u8>) + Send + Sync>,
297}
298
299impl<T: State> StateBinding<T> {
300    /// Create a new binding from a [`PluginContext`]. Generic over the
301    /// context's `<P>` since `StateBinding` cares only about the
302    /// `get_state` / `set_state` channel on the underlying
303    /// `EditorBridge`, never about parameter typing.
304    #[must_use]
305    pub fn new<P: ?Sized>(context: &PluginContext<P>) -> Self {
306        let bridge_for_get = Arc::clone(context.bridge());
307        let bridge_for_set = Arc::clone(context.bridge());
308        let mut binding = Self {
309            cached: T::default(),
310            get_state: Arc::new(move || bridge_for_get.get_state()),
311            set_state: Arc::new(move |data| bridge_for_set.set_state(data)),
312        };
313        binding.sync();
314        binding
315    }
316
317    /// Re-read state from the plugin. Call this from `state_changed()`.
318    pub fn sync(&mut self) {
319        let data = (self.get_state)();
320        if !data.is_empty()
321            && let Some(s) = T::deserialize(&data)
322        {
323            self.cached = s;
324        }
325    }
326
327    /// Get the current cached state.
328    pub fn get(&self) -> &T {
329        &self.cached
330    }
331
332    /// Modify state and write it back to the plugin.
333    pub fn update(&mut self, f: impl FnOnce(&mut T)) {
334        f(&mut self.cached);
335        let data = self.cached.serialize();
336        (self.set_state)(data);
337    }
338}
339
340impl<T: State> Default for StateBinding<T> {
341    /// Construct an **unwired** binding: `get()` returns `T::default()`
342    /// and `update()` *silently discards* the new state. Only useful
343    /// as a placeholder before the editor is opened; replace with
344    /// [`StateBinding::new(&context)`](StateBinding::new) inside
345    /// `Editor::open` once a [`PluginContext`] is available. If you
346    /// see writes vanishing, check that the binding has been wired up
347    /// before you call `update`.
348    fn default() -> Self {
349        Self {
350            cached: T::default(),
351            get_state: Arc::new(Vec::new),
352            set_state: Arc::new(|_| {}),
353        }
354    }
355}
356
357// ---------------------------------------------------------------------------
358// Tests
359// ---------------------------------------------------------------------------
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364
365    #[test]
366    fn primitives_round_trip() {
367        let mut buf = Vec::new();
368        42u32.write_field(&mut buf);
369        2.5f64.write_field(&mut buf);
370        true.write_field(&mut buf);
371
372        let mut cursor = StateCursor::new(&buf);
373        assert_eq!(u32::read_field(&mut cursor), Some(42));
374        assert_eq!(f64::read_field(&mut cursor), Some(2.5));
375        assert_eq!(bool::read_field(&mut cursor), Some(true));
376    }
377
378    #[test]
379    fn string_round_trip() {
380        let mut buf = Vec::new();
381        "hello world".to_string().write_field(&mut buf);
382
383        let mut cursor = StateCursor::new(&buf);
384        assert_eq!(
385            String::read_field(&mut cursor),
386            Some("hello world".to_string())
387        );
388    }
389
390    #[test]
391    fn vec_round_trip() {
392        let mut buf = Vec::new();
393        vec![1u32, 2, 3].write_field(&mut buf);
394
395        let mut cursor = StateCursor::new(&buf);
396        assert_eq!(Vec::<u32>::read_field(&mut cursor), Some(vec![1, 2, 3]));
397    }
398
399    #[test]
400    fn option_round_trip() {
401        let mut buf = Vec::new();
402        Some(42u32).write_field(&mut buf);
403        None::<u32>.write_field(&mut buf);
404
405        let mut cursor = StateCursor::new(&buf);
406        assert_eq!(Option::<u32>::read_field(&mut cursor), Some(Some(42)));
407        assert_eq!(Option::<u32>::read_field(&mut cursor), Some(None));
408    }
409
410    #[test]
411    fn nested_vec_string() {
412        let mut buf = Vec::new();
413        let v = vec!["foo".to_string(), "bar".to_string()];
414        v.write_field(&mut buf);
415
416        let mut cursor = StateCursor::new(&buf);
417        assert_eq!(Vec::<String>::read_field(&mut cursor), Some(v));
418    }
419
420    #[test]
421    fn persist_field_lock_round_trip() {
422        let src = std::sync::RwLock::new("guitar bus".to_string());
423        let mut buf = Vec::new();
424        src.persist_write(&mut buf);
425
426        let dst = std::sync::RwLock::new(String::new());
427        dst.persist_read(&mut StateCursor::new(&buf));
428        assert_eq!(*dst.read().unwrap(), "guitar bus");
429
430        let m_src = std::sync::Mutex::new(vec![1u32, 2, 3]);
431        let mut m_buf = Vec::new();
432        m_src.persist_write(&mut m_buf);
433        let m_dst = std::sync::Mutex::new(Vec::<u32>::new());
434        m_dst.persist_read(&mut StateCursor::new(&m_buf));
435        assert_eq!(*m_dst.lock().unwrap(), vec![1, 2, 3]);
436    }
437
438    #[test]
439    fn persist_field_atomic_cell_round_trip() {
440        let src = AtomicCell::new(0.75f32);
441        let mut buf = Vec::new();
442        src.persist_write(&mut buf);
443
444        let dst = AtomicCell::new(0.0f32);
445        dst.persist_read(&mut StateCursor::new(&buf));
446        // Bit-exact: the value round-trips through `to_le_bytes`.
447        assert_eq!(dst.load().to_bits(), 0.75f32.to_bits());
448    }
449
450    #[test]
451    fn persist_field_leaves_value_on_short_read() {
452        // A truncated blob must not clobber the current value.
453        let dst = AtomicCell::new(42u32);
454        dst.persist_read(&mut StateCursor::new(&[0xFF, 0xFF]));
455        assert_eq!(dst.load(), 42);
456    }
457}