runmat_vm/runtime/
workspace.rs1use runmat_builtins::Value;
2use runmat_thread_local::runmat_thread_local;
3use std::cell::RefCell;
4use std::collections::{HashMap, HashSet};
5
6struct WorkspaceState {
7 names: HashMap<String, usize>,
8 assigned: HashSet<String>,
9 idx_to_name: HashMap<usize, String>,
10 data_ptr: *const Value,
11 len: usize,
12}
13
14pub type WorkspaceSnapshot = (HashMap<String, usize>, HashSet<String>);
15
16runmat_thread_local! {
17 static WORKSPACE_STATE: RefCell<Option<WorkspaceState>> = const { RefCell::new(None) };
18 static PENDING_WORKSPACE: RefCell<Option<WorkspaceSnapshot>> = const { RefCell::new(None) };
19 static LAST_WORKSPACE_STATE: RefCell<Option<WorkspaceSnapshot>> = const { RefCell::new(None) };
20 static WORKSPACE_VARS: RefCell<Option<*mut Vec<Value>>> = const { RefCell::new(None) };
21}
22
23pub struct WorkspaceStateGuard;
24
25impl Drop for WorkspaceStateGuard {
26 fn drop(&mut self) {
27 WORKSPACE_STATE.with(|state| {
28 let mut state_mut = state.borrow_mut();
29 if let Some(ws) = state_mut.take() {
30 LAST_WORKSPACE_STATE.with(|slot| {
31 *slot.borrow_mut() = Some((ws.names, ws.assigned));
32 });
33 }
34 });
35 WORKSPACE_VARS.with(|slot| {
36 slot.borrow_mut().take();
37 });
38 }
39}
40
41pub struct PendingWorkspaceGuard;
42
43impl Drop for PendingWorkspaceGuard {
44 fn drop(&mut self) {
45 PENDING_WORKSPACE.with(|slot| {
46 slot.borrow_mut().take();
47 });
48 }
49}
50
51pub fn push_pending_workspace(
52 names: HashMap<String, usize>,
53 assigned: HashSet<String>,
54) -> PendingWorkspaceGuard {
55 PENDING_WORKSPACE.with(|slot| {
56 *slot.borrow_mut() = Some((names, assigned));
57 });
58 PendingWorkspaceGuard
59}
60
61pub fn take_pending_workspace_state() -> Option<WorkspaceSnapshot> {
62 PENDING_WORKSPACE.with(|slot| slot.borrow_mut().take())
63}
64
65pub fn take_updated_workspace_state() -> Option<WorkspaceSnapshot> {
66 LAST_WORKSPACE_STATE.with(|slot| slot.borrow_mut().take())
67}
68
69pub fn set_workspace_state(
70 names: HashMap<String, usize>,
71 assigned: HashSet<String>,
72 vars: &mut Vec<Value>,
73) -> WorkspaceStateGuard {
74 let idx_to_name: HashMap<usize, String> = names.iter().map(|(k, &v)| (v, k.clone())).collect();
75 WORKSPACE_STATE.with(|state| {
76 *state.borrow_mut() = Some(WorkspaceState {
77 names,
78 assigned,
79 idx_to_name,
80 data_ptr: vars.as_ptr(),
81 len: vars.len(),
82 });
83 });
84 let vars_ptr = vars as *mut Vec<Value>;
85 WORKSPACE_VARS.with(|slot| {
86 *slot.borrow_mut() = Some(vars_ptr);
87 });
88 WorkspaceStateGuard
89}
90
91pub fn refresh_workspace_state(vars: &[Value]) {
92 WORKSPACE_STATE.with(|state| {
93 if let Some(ws) = state.borrow_mut().as_mut() {
94 ws.data_ptr = vars.as_ptr();
95 ws.len = vars.len();
96 }
97 });
98}
99
100pub fn workspace_lookup(name: &str) -> Option<Value> {
101 WORKSPACE_STATE.with(|state| {
102 let state_ref = state.borrow();
103 let ws = state_ref.as_ref()?;
104 let idx = ws.names.get(name)?;
105 if !ws.assigned.contains(name) {
106 return None;
107 }
108 if *idx >= ws.len {
109 return None;
110 }
111 unsafe {
112 let ptr = ws.data_ptr.add(*idx);
113 Some((*ptr).clone())
114 }
115 })
116}
117
118pub fn workspace_slot_assigned(index: usize) -> Option<bool> {
119 WORKSPACE_STATE.with(|state| {
120 let state_ref = state.borrow();
121 let ws = state_ref.as_ref()?;
122 let name = ws.idx_to_name.get(&index)?;
123 Some(ws.assigned.contains(name))
124 })
125}
126
127pub fn workspace_state_available() -> bool {
128 WORKSPACE_STATE.with(|state| state.borrow().is_some())
129}
130
131pub fn workspace_assign(name: &str, value: Value) -> Result<(), String> {
132 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
133 let Some(vars_ptr) = vars_ptr else {
134 return Err("load: workspace state unavailable".to_string());
135 };
136 let vars = unsafe { &mut *vars_ptr };
137 set_workspace_variable(name, value, vars)
138}
139
140pub fn workspace_clear() -> Result<(), String> {
141 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
142 let Some(vars_ptr) = vars_ptr else {
143 return Err("clear: workspace state unavailable".to_string());
144 };
145 let vars = unsafe { &mut *vars_ptr };
146
147 WORKSPACE_STATE.with(|state| {
148 let mut state_mut = state.borrow_mut();
149 let Some(ws) = state_mut.as_mut() else {
150 return Err("clear: workspace state unavailable".to_string());
151 };
152 vars.clear();
153 ws.names.clear();
154 ws.assigned.clear();
155 ws.idx_to_name.clear();
156 ws.data_ptr = vars.as_ptr();
157 ws.len = vars.len();
158 Ok(())
159 })
160}
161
162pub fn workspace_remove(name: &str) -> Result<(), String> {
163 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
164 let Some(vars_ptr) = vars_ptr else {
165 return Err("clear: workspace state unavailable".to_string());
166 };
167 let vars = unsafe { &mut *vars_ptr };
168
169 WORKSPACE_STATE.with(|state| {
170 let mut state_mut = state.borrow_mut();
171 let Some(ws) = state_mut.as_mut() else {
172 return Err("clear: workspace state unavailable".to_string());
173 };
174 if let Some(idx) = ws.names.remove(name) {
175 if idx < vars.len() {
176 vars[idx] = Value::Num(0.0);
177 }
178 ws.assigned.remove(name);
179 ws.idx_to_name.remove(&idx);
180 ws.data_ptr = vars.as_ptr();
181 ws.len = vars.len();
182 }
183 Ok(())
184 })
185}
186
187pub fn workspace_snapshot() -> Vec<(String, Value)> {
188 WORKSPACE_STATE.with(|state| {
189 if let Some(ws) = state.borrow().as_ref() {
190 let mut entries: Vec<(String, Value)> = ws
191 .names
192 .iter()
193 .filter_map(|(name, idx)| {
194 if *idx >= ws.len {
195 return None;
196 }
197 if !ws.assigned.contains(name) {
198 return None;
199 }
200 unsafe {
201 let ptr = ws.data_ptr.add(*idx);
202 Some((name.clone(), (*ptr).clone()))
203 }
204 })
205 .collect();
206 entries.sort_by(|a, b| a.0.cmp(&b.0));
207 entries
208 } else {
209 Vec::new()
210 }
211 })
212}
213
214pub fn set_workspace_variable(
215 name: &str,
216 value: Value,
217 vars: &mut Vec<Value>,
218) -> Result<(), String> {
219 let mut result = Ok(());
220 WORKSPACE_STATE.with(|state| {
221 let mut state_mut = state.borrow_mut();
222 match state_mut.as_mut() {
223 Some(ws) => {
224 let idx = if let Some(idx) = ws.names.get(name).copied() {
225 idx
226 } else {
227 let idx = vars.len();
228 ws.names.insert(name.to_string(), idx);
229 ws.idx_to_name.insert(idx, name.to_string());
230 idx
231 };
232 if idx >= vars.len() {
233 vars.resize(idx + 1, Value::Num(0.0));
234 }
235 vars[idx] = value;
236 ws.data_ptr = vars.as_ptr();
237 ws.len = vars.len();
238 ws.assigned.insert(name.to_string());
239 }
240 None => {
241 result = Err("load: workspace state unavailable".to_string());
242 }
243 }
244 });
245 result
246}
247
248pub fn ensure_workspace_slot_name(index: usize, name: &str) {
249 WORKSPACE_STATE.with(|state| {
250 if let Some(ws) = state.borrow_mut().as_mut() {
251 ws.names.entry(name.to_string()).or_insert(index);
252 ws.idx_to_name
253 .entry(index)
254 .or_insert_with(|| name.to_string());
255 }
256 });
257}
258
259pub fn mark_workspace_assigned(index: usize) {
260 WORKSPACE_STATE.with(|state| {
261 if let Some(ws) = state.borrow_mut().as_mut() {
262 if let Some(name) = ws.idx_to_name.get(&index).cloned() {
263 ws.assigned.insert(name);
264 }
265 }
266 });
267}