Skip to main content

maolan_engine/
state.rs

1use crate::message::{AudioClipData, MidiClipData};
2use crate::track::Track;
3use std::{
4    cell::UnsafeCell,
5    collections::HashMap,
6    fmt,
7    marker::PhantomData,
8    ops::{Deref, DerefMut},
9    sync::Arc,
10};
11
12pub type TrackHandle = Arc<Track>;
13pub type StateSlot = arc_swap::ArcSwap<StateSnapshot>;
14
15pub struct State {
16    inner: UnsafeCell<StateData>,
17}
18
19#[derive(Default, Debug)]
20pub struct StateData {
21    pub tracks: HashMap<String, TrackHandle>,
22    pub unused_audio_clips: Vec<AudioClipData>,
23    pub unused_midi_clips: Vec<MidiClipData>,
24}
25
26pub struct StateGuard<'a> {
27    ptr: *mut StateData,
28    _marker: PhantomData<&'a mut StateData>,
29}
30
31unsafe impl Send for StateGuard<'_> {}
32
33// SAFETY: `State` preserves the legacy engine invariant that mutation is
34// externally serialized by the control/runtime path. `lock` returns a guard to
35// make interior mutation explicit without retagging a shared reference as
36// unique, which Miri rightfully rejects.
37unsafe impl Sync for State {}
38
39impl Default for State {
40    fn default() -> Self {
41        Self {
42            inner: UnsafeCell::new(StateData::default()),
43        }
44    }
45}
46
47impl fmt::Debug for State {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.debug_struct("State")
50            .field("tracks", &self.tracks)
51            .finish()
52    }
53}
54
55impl Deref for State {
56    type Target = StateData;
57
58    fn deref(&self) -> &Self::Target {
59        unsafe { &*self.inner.get() }
60    }
61}
62
63impl DerefMut for State {
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        self.inner.get_mut()
66    }
67}
68
69impl StateData {
70    pub fn snapshot(&self) -> StateSnapshot {
71        StateSnapshot {
72            tracks: self.tracks.clone(),
73            unused_audio_clips: self.unused_audio_clips.clone(),
74            unused_midi_clips: self.unused_midi_clips.clone(),
75        }
76    }
77}
78
79impl Deref for StateGuard<'_> {
80    type Target = StateData;
81
82    fn deref(&self) -> &Self::Target {
83        unsafe { &*self.ptr }
84    }
85}
86
87impl DerefMut for StateGuard<'_> {
88    fn deref_mut(&mut self) -> &mut Self::Target {
89        unsafe { &mut *self.ptr }
90    }
91}
92
93#[derive(Clone, Default, Debug)]
94pub struct StateSnapshot {
95    pub tracks: HashMap<String, TrackHandle>,
96    pub unused_audio_clips: Vec<AudioClipData>,
97    pub unused_midi_clips: Vec<MidiClipData>,
98}
99
100impl State {
101    pub fn lock(&self) -> StateGuard<'_> {
102        StateGuard {
103            ptr: self.inner.get(),
104            _marker: PhantomData,
105        }
106    }
107
108    pub fn snapshot(&self) -> StateSnapshot {
109        StateData::snapshot(self)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn state_default_creates_empty() {
119        let state = State::default();
120        assert!(state.lock().tracks.is_empty());
121    }
122
123    #[test]
124    fn state_debug_format() {
125        let state = State::default();
126        let debug_str = format!("{:?}", state);
127        assert!(debug_str.contains("State"));
128        assert!(debug_str.contains("tracks"));
129    }
130
131    #[test]
132    fn state_new_is_default() {
133        let state1 = State::default();
134        let state2 = State::default();
135        assert_eq!(state1.lock().tracks.len(), state2.lock().tracks.len());
136    }
137
138    #[test]
139    fn state_snapshot_clones_track_handles() {
140        let state = State::default();
141        let track = Arc::new(Track::new("track".to_string(), 1, 1, 0, 0, 64, 48_000.0));
142        state
143            .lock()
144            .tracks
145            .insert("track".to_string(), track.clone());
146
147        let snapshot = state.snapshot();
148
149        assert!(Arc::ptr_eq(snapshot.tracks.get("track").unwrap(), &track));
150    }
151}