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_assign(name: &str, value: Value) -> Result<(), String> {
119 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
120 let Some(vars_ptr) = vars_ptr else {
121 return Err("load: workspace state unavailable".to_string());
122 };
123 let vars = unsafe { &mut *vars_ptr };
124 set_workspace_variable(name, value, vars)
125}
126
127pub fn workspace_clear() -> Result<(), String> {
128 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
129 let Some(vars_ptr) = vars_ptr else {
130 return Err("clear: workspace state unavailable".to_string());
131 };
132 let vars = unsafe { &mut *vars_ptr };
133
134 WORKSPACE_STATE.with(|state| {
135 let mut state_mut = state.borrow_mut();
136 let Some(ws) = state_mut.as_mut() else {
137 return Err("clear: workspace state unavailable".to_string());
138 };
139 vars.clear();
140 ws.names.clear();
141 ws.assigned.clear();
142 ws.idx_to_name.clear();
143 ws.data_ptr = vars.as_ptr();
144 ws.len = vars.len();
145 Ok(())
146 })
147}
148
149pub fn workspace_remove(name: &str) -> Result<(), String> {
150 let vars_ptr = WORKSPACE_VARS.with(|slot| *slot.borrow());
151 let Some(vars_ptr) = vars_ptr else {
152 return Err("clear: workspace state unavailable".to_string());
153 };
154 let vars = unsafe { &mut *vars_ptr };
155
156 WORKSPACE_STATE.with(|state| {
157 let mut state_mut = state.borrow_mut();
158 let Some(ws) = state_mut.as_mut() else {
159 return Err("clear: workspace state unavailable".to_string());
160 };
161 if let Some(idx) = ws.names.remove(name) {
162 if idx < vars.len() {
163 vars[idx] = Value::Num(0.0);
164 }
165 ws.assigned.remove(name);
166 ws.idx_to_name.remove(&idx);
167 ws.data_ptr = vars.as_ptr();
168 ws.len = vars.len();
169 }
170 Ok(())
171 })
172}
173
174pub fn workspace_snapshot() -> Vec<(String, Value)> {
175 WORKSPACE_STATE.with(|state| {
176 if let Some(ws) = state.borrow().as_ref() {
177 let mut entries: Vec<(String, Value)> = ws
178 .names
179 .iter()
180 .filter_map(|(name, idx)| {
181 if *idx >= ws.len {
182 return None;
183 }
184 if !ws.assigned.contains(name) {
185 return None;
186 }
187 unsafe {
188 let ptr = ws.data_ptr.add(*idx);
189 Some((name.clone(), (*ptr).clone()))
190 }
191 })
192 .collect();
193 entries.sort_by(|a, b| a.0.cmp(&b.0));
194 entries
195 } else {
196 Vec::new()
197 }
198 })
199}
200
201pub fn set_workspace_variable(
202 name: &str,
203 value: Value,
204 vars: &mut Vec<Value>,
205) -> Result<(), String> {
206 let mut result = Ok(());
207 WORKSPACE_STATE.with(|state| {
208 let mut state_mut = state.borrow_mut();
209 match state_mut.as_mut() {
210 Some(ws) => {
211 let idx = if let Some(idx) = ws.names.get(name).copied() {
212 idx
213 } else {
214 let idx = vars.len();
215 ws.names.insert(name.to_string(), idx);
216 ws.idx_to_name.insert(idx, name.to_string());
217 idx
218 };
219 if idx >= vars.len() {
220 vars.resize(idx + 1, Value::Num(0.0));
221 }
222 vars[idx] = value;
223 ws.data_ptr = vars.as_ptr();
224 ws.len = vars.len();
225 ws.assigned.insert(name.to_string());
226 }
227 None => {
228 result = Err("load: workspace state unavailable".to_string());
229 }
230 }
231 });
232 result
233}
234
235pub fn ensure_workspace_slot_name(index: usize, name: &str) {
236 WORKSPACE_STATE.with(|state| {
237 if let Some(ws) = state.borrow_mut().as_mut() {
238 ws.names.entry(name.to_string()).or_insert(index);
239 ws.idx_to_name
240 .entry(index)
241 .or_insert_with(|| name.to_string());
242 }
243 });
244}
245
246pub fn mark_workspace_assigned(index: usize) {
247 WORKSPACE_STATE.with(|state| {
248 if let Some(ws) = state.borrow_mut().as_mut() {
249 if let Some(name) = ws.idx_to_name.get(&index).cloned() {
250 ws.assigned.insert(name);
251 }
252 }
253 });
254}