1use std::sync::{Arc, Mutex};
31
32use arc_swap::ArcSwap;
33use cljrs_gc::{GcPtr, StaticGcPtr};
34
35use crate::intern::{intern_keyword, intern_symbol};
36use crate::keyword::Keyword;
37use crate::symbol::Symbol;
38use crate::value::Value;
39
40#[derive(Debug, Clone)]
44pub struct PromoteError {
45 pub type_name: &'static str,
46}
47
48impl PromoteError {
49 pub fn not_promotable(type_name: &'static str) -> Self {
50 Self { type_name }
51 }
52}
53
54impl std::fmt::Display for PromoteError {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(
57 f,
58 "value of type '{}' cannot be promoted to a shared-atom: \
59 only scalars, strings, keywords, symbols, and byte-blobs are supported",
60 self.type_name
61 )
62 }
63}
64
65impl std::error::Error for PromoteError {}
66
67#[derive(Debug, Clone)]
79pub enum SharedValue {
80 Nil,
81 Bool(bool),
82 Long(i64),
83 Double(f64),
84 Char(char),
85 Uuid(u128),
86 Str(Arc<str>),
88 Keyword(StaticGcPtr<Keyword>),
90 Symbol(StaticGcPtr<Symbol>),
92 ByteBlob(Arc<[u8]>),
96}
97
98unsafe impl Send for SharedValue {}
101unsafe impl Sync for SharedValue {}
102
103impl SharedValue {
104 pub fn type_name(&self) -> &'static str {
105 match self {
106 SharedValue::Nil => "nil",
107 SharedValue::Bool(_) => "boolean",
108 SharedValue::Long(_) => "long",
109 SharedValue::Double(_) => "double",
110 SharedValue::Char(_) => "char",
111 SharedValue::Uuid(_) => "uuid",
112 SharedValue::Str(_) => "string",
113 SharedValue::Keyword(_) => "keyword",
114 SharedValue::Symbol(_) => "symbol",
115 SharedValue::ByteBlob(_) => "byte-blob",
116 }
117 }
118}
119
120pub fn promote(value: &Value) -> Result<SharedValue, PromoteError> {
129 match value {
130 Value::Nil => Ok(SharedValue::Nil),
131 Value::Bool(b) => Ok(SharedValue::Bool(*b)),
132 Value::Long(n) => Ok(SharedValue::Long(*n)),
133 Value::Double(d) => Ok(SharedValue::Double(*d)),
134 Value::Char(c) => Ok(SharedValue::Char(*c)),
135 Value::Uuid(u) => Ok(SharedValue::Uuid(*u)),
136 Value::Str(s) => Ok(SharedValue::Str(Arc::from(s.get().as_str()))),
137 Value::Keyword(kw) => {
138 let kw = kw.get();
139 let ptr = intern_keyword(kw.namespace.as_deref(), &kw.name);
140 Ok(SharedValue::Keyword(ptr))
141 }
142 Value::Symbol(sym) => {
143 let sym = sym.get();
144 let ptr = intern_symbol(sym.namespace.as_deref(), &sym.name, sym.version.as_deref());
145 Ok(SharedValue::Symbol(ptr))
146 }
147 Value::ByteArray(arr) => {
148 let bytes = arr.get().lock().unwrap();
149 let blob: Arc<[u8]> = bytes.iter().map(|&b| b as u8).collect::<Vec<_>>().into();
150 Ok(SharedValue::ByteBlob(blob))
151 }
152 Value::ByteBlob(blob) => Ok(SharedValue::ByteBlob(blob.clone())),
153 other => Err(PromoteError::not_promotable(other.type_name())),
154 }
155}
156
157pub fn demote(sv: &SharedValue) -> Value {
164 match sv {
165 SharedValue::Nil => Value::Nil,
166 SharedValue::Bool(b) => Value::Bool(*b),
167 SharedValue::Long(n) => Value::Long(*n),
168 SharedValue::Double(d) => Value::Double(*d),
169 SharedValue::Char(c) => Value::Char(*c),
170 SharedValue::Uuid(u) => Value::Uuid(*u),
171 SharedValue::Str(s) => Value::Str(GcPtr::new(s.as_ref().to_owned())),
172 SharedValue::Keyword(kw) => Value::Keyword(GcPtr::new(kw.get().clone())),
173 SharedValue::Symbol(sym) => Value::Symbol(GcPtr::new(sym.get().clone())),
174 SharedValue::ByteBlob(blob) => Value::ByteBlob(blob.clone()),
175 }
176}
177
178#[derive(Debug)]
191pub struct SharedAtom {
192 pub cell: Arc<ArcSwap<SharedValue>>,
193 pub meta: Mutex<Option<SharedValue>>,
194}
195
196impl SharedAtom {
197 pub fn new(val: SharedValue) -> Self {
198 Self {
199 cell: Arc::new(ArcSwap::new(Arc::new(val))),
200 meta: Mutex::new(None),
201 }
202 }
203
204 pub fn deref_val(&self) -> Arc<SharedValue> {
206 self.cell.load_full()
207 }
208
209 pub fn reset(&self, val: SharedValue) -> Arc<SharedValue> {
211 let arc = Arc::new(val);
212 self.cell.store(arc.clone());
213 arc
214 }
215
216 pub fn swap<F>(&self, mut f: F) -> Arc<SharedValue>
219 where
220 F: FnMut(&SharedValue) -> SharedValue,
221 {
222 self.cell.rcu(|old| Arc::new(f(old)))
223 }
224
225 pub fn compare_and_set(&self, current: &Arc<SharedValue>, new: SharedValue) -> bool {
234 let prev = self.cell.compare_and_swap(current, Arc::new(new));
235 std::ptr::eq(Arc::as_ptr(current), Arc::as_ptr(&prev))
237 }
238}
239
240#[cfg(test)]
243mod tests {
244 use std::sync::Arc;
245
246 use crate::keyword::Keyword;
247 use crate::value::Value;
248 use cljrs_gc::GcPtr;
249
250 use super::*;
251
252 fn kw(name: &str) -> Value {
253 Value::Keyword(GcPtr::new(Keyword::simple(name)))
254 }
255
256 #[test]
257 fn promote_scalars() {
258 assert!(matches!(promote(&Value::Nil), Ok(SharedValue::Nil)));
259 assert!(matches!(
260 promote(&Value::Bool(true)),
261 Ok(SharedValue::Bool(true))
262 ));
263 assert!(matches!(
264 promote(&Value::Long(42)),
265 Ok(SharedValue::Long(42))
266 ));
267 }
268
269 #[test]
270 fn promote_keyword_interns() {
271 let v = kw("foo");
272 let sv = promote(&v).unwrap();
273 let sv2 = promote(&kw("foo")).unwrap();
274 if let (SharedValue::Keyword(a), SharedValue::Keyword(b)) = (sv, sv2) {
275 assert!(
276 cljrs_gc::StaticGcPtr::ptr_eq(&a, &b),
277 "same keyword should intern to same StaticGcPtr"
278 );
279 } else {
280 panic!("expected SharedValue::Keyword");
281 }
282 }
283
284 #[test]
285 fn demote_roundtrip_long() {
286 let sv = SharedValue::Long(99);
287 assert!(matches!(demote(&sv), Value::Long(99)));
288 }
289
290 #[test]
291 fn demote_roundtrip_keyword() {
292 let sv = promote(&kw("test")).unwrap();
293 let v = demote(&sv);
294 if let Value::Keyword(kw_ptr) = v {
295 assert_eq!(kw_ptr.get().name.as_ref(), "test");
296 } else {
297 panic!("expected Value::Keyword");
298 }
299 }
300
301 #[test]
302 fn promote_non_promotable_returns_err() {
303 let atom = Value::Atom(GcPtr::new(crate::types::Atom::new(Value::Nil)));
304 assert!(promote(&atom).is_err());
305 }
306
307 #[test]
308 fn promote_byte_blob() {
309 let arr: Vec<i8> = vec![1, 2, 3];
310 let v = Value::ByteArray(GcPtr::new(std::sync::Mutex::new(arr)));
311 let sv = promote(&v).unwrap();
312 assert!(matches!(sv, SharedValue::ByteBlob(_)));
313 }
314
315 #[test]
316 fn shared_atom_reset_and_deref() {
317 let atom = SharedAtom::new(SharedValue::Long(0));
318 atom.reset(SharedValue::Long(42));
319 let val = atom.deref_val();
320 assert!(matches!(val.as_ref(), SharedValue::Long(42)));
321 }
322
323 #[test]
324 fn shared_atom_swap() {
325 let atom = SharedAtom::new(SharedValue::Long(1));
326 atom.swap(|old| {
327 if let SharedValue::Long(n) = old {
328 SharedValue::Long(n + 1)
329 } else {
330 SharedValue::Long(0)
331 }
332 });
333 let val = atom.deref_val();
334 assert!(matches!(val.as_ref(), SharedValue::Long(2)));
335 }
336
337 #[test]
338 fn shared_atom_compare_and_set() {
339 let atom = SharedAtom::new(SharedValue::Long(1));
340 let cur = atom.deref_val();
341 assert!(atom.compare_and_set(&cur, SharedValue::Long(2)));
343 assert!(matches!(atom.deref_val().as_ref(), SharedValue::Long(2)));
344 assert!(!atom.compare_and_set(&cur, SharedValue::Long(99)));
346 assert!(matches!(atom.deref_val().as_ref(), SharedValue::Long(2)));
347 }
348
349 #[test]
350 fn shared_atom_is_send_sync() {
351 fn assert_send_sync<T: Send + Sync>() {}
352 assert_send_sync::<SharedAtom>();
353 assert_send_sync::<Arc<SharedAtom>>();
354 }
355
356 #[test]
357 fn byte_blob_shared_across_clone() {
358 let blob: Arc<[u8]> = vec![10u8, 20, 30].into();
359 let v1 = Value::ByteBlob(blob.clone());
360 let v2 = Value::ByteBlob(blob.clone());
361 if let (Value::ByteBlob(a), Value::ByteBlob(b)) = (&v1, &v2) {
363 assert!(Arc::ptr_eq(a, b));
364 }
365 }
366}