1use std::ops::Deref;
38use std::sync::{Arc, Mutex};
39
40use arc_swap::ArcSwap;
41use cljrs_gc::{GcPtr, MemoryCharge, MemoryClass, MemoryLimitExceeded, StaticGcPtr, governor};
42
43use crate::intern::{intern_keyword, intern_symbol};
44use crate::keyword::Keyword;
45use crate::symbol::Symbol;
46use crate::value::Value;
47
48#[derive(Debug, Clone)]
52pub struct PromoteError {
53 pub type_name: &'static str,
54 pub memory: Option<MemoryLimitExceeded>,
57}
58
59impl PromoteError {
60 pub fn not_promotable(type_name: &'static str) -> Self {
61 Self {
62 type_name,
63 memory: None,
64 }
65 }
66
67 pub fn memory_limit(type_name: &'static str, error: MemoryLimitExceeded) -> Self {
69 Self {
70 type_name,
71 memory: Some(error),
72 }
73 }
74}
75
76impl PromoteError {
77 pub fn into_value_error(self) -> crate::error::ValueError {
80 match self.memory {
81 Some(limit) => crate::error::ValueError::MemoryLimitExceeded(limit),
82 None => crate::error::ValueError::Other(self.to_string()),
83 }
84 }
85}
86
87impl std::fmt::Display for PromoteError {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 if let Some(limit) = &self.memory {
90 return limit.fmt(f);
91 }
92 write!(
93 f,
94 "value of type '{}' cannot be promoted to a shared-atom: \
95 only scalars, strings, keywords, symbols, and byte-blobs are supported",
96 self.type_name
97 )
98 }
99}
100
101impl std::error::Error for PromoteError {}
102
103#[derive(Debug, Clone)]
115pub enum SharedValue {
116 Nil,
117 Bool(bool),
118 Long(i64),
119 Double(f64),
120 Char(char),
121 Uuid(u128),
122 Str(Arc<str>),
124 Keyword(StaticGcPtr<Keyword>),
126 Symbol(StaticGcPtr<Symbol>),
128 ByteBlob(Arc<ByteBlob>),
132}
133
134unsafe impl Send for SharedValue {}
137unsafe impl Sync for SharedValue {}
138
139impl SharedValue {
140 pub fn type_name(&self) -> &'static str {
141 match self {
142 SharedValue::Nil => "nil",
143 SharedValue::Bool(_) => "boolean",
144 SharedValue::Long(_) => "long",
145 SharedValue::Double(_) => "double",
146 SharedValue::Char(_) => "char",
147 SharedValue::Uuid(_) => "uuid",
148 SharedValue::Str(_) => "string",
149 SharedValue::Keyword(_) => "keyword",
150 SharedValue::Symbol(_) => "symbol",
151 SharedValue::ByteBlob(_) => "byte-blob",
152 }
153 }
154}
155
156const fn arc_bytes<T>() -> usize {
160 2 * std::mem::size_of::<usize>() + std::mem::size_of::<T>()
161}
162
163fn reserve(bytes: usize) -> MemoryCharge {
168 governor().reserve_infallible(MemoryClass::SharedValue, bytes)
169}
170
171pub struct ByteBlob {
176 bytes: Box<[u8]>,
177 charge: MemoryCharge,
178}
179
180impl ByteBlob {
181 pub fn new(bytes: impl Into<Box<[u8]>>) -> Self {
185 let bytes = bytes.into();
186 let charge = reserve(arc_bytes::<ByteBlob>() + bytes.len());
187 Self { bytes, charge }
188 }
189
190 pub fn try_new(bytes: impl Into<Box<[u8]>>) -> Result<Self, MemoryLimitExceeded> {
193 let bytes = bytes.into();
194 let charge = governor().reserve_shared(
195 MemoryClass::SharedValue,
196 arc_bytes::<ByteBlob>() + bytes.len(),
197 )?;
198 Ok(Self { bytes, charge })
199 }
200
201 pub fn try_with(
204 len: usize,
205 fill: impl FnOnce() -> Box<[u8]>,
206 ) -> Result<Self, MemoryLimitExceeded> {
207 let charge =
208 governor().reserve_shared(MemoryClass::SharedValue, arc_bytes::<ByteBlob>() + len)?;
209 Ok(Self {
210 bytes: fill(),
211 charge,
212 })
213 }
214
215 pub fn as_bytes(&self) -> &[u8] {
217 &self.bytes
218 }
219
220 pub fn charged_bytes(&self) -> usize {
222 self.charge.bytes()
223 }
224}
225
226impl Deref for ByteBlob {
227 type Target = [u8];
228 fn deref(&self) -> &[u8] {
229 &self.bytes
230 }
231}
232
233impl AsRef<[u8]> for ByteBlob {
234 fn as_ref(&self) -> &[u8] {
235 &self.bytes
236 }
237}
238
239impl From<Vec<u8>> for ByteBlob {
240 fn from(bytes: Vec<u8>) -> Self {
241 Self::new(bytes)
242 }
243}
244
245impl From<&[u8]> for ByteBlob {
246 fn from(bytes: &[u8]) -> Self {
247 Self::new(bytes)
248 }
249}
250
251impl std::fmt::Debug for ByteBlob {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 write!(f, "ByteBlob({} bytes)", self.bytes.len())
254 }
255}
256
257pub struct SharedSlot {
261 value: SharedValue,
262 charge: MemoryCharge,
263}
264
265impl SharedSlot {
266 pub fn new(value: SharedValue) -> Self {
270 let charge = reserve(Self::bytes_for(&value));
271 Self { value, charge }
272 }
273
274 pub fn try_new(value: SharedValue) -> Result<Self, MemoryLimitExceeded> {
276 let charge =
277 governor().reserve_shared(MemoryClass::SharedValue, Self::bytes_for(&value))?;
278 Ok(Self { value, charge })
279 }
280
281 fn bytes_for(value: &SharedValue) -> usize {
282 let payload = match value {
283 SharedValue::Str(s) => s.len(),
284 _ => 0,
285 };
286 arc_bytes::<SharedSlot>() + payload
287 }
288
289 pub fn value(&self) -> &SharedValue {
291 &self.value
292 }
293
294 pub fn charged_bytes(&self) -> usize {
296 self.charge.bytes()
297 }
298}
299
300impl Deref for SharedSlot {
301 type Target = SharedValue;
302 fn deref(&self) -> &SharedValue {
303 &self.value
304 }
305}
306
307impl AsRef<SharedValue> for SharedSlot {
308 fn as_ref(&self) -> &SharedValue {
309 &self.value
310 }
311}
312
313impl std::fmt::Debug for SharedSlot {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 self.value.fmt(f)
316 }
317}
318
319pub fn promote(value: &Value) -> Result<SharedValue, PromoteError> {
328 match value {
329 Value::Nil => Ok(SharedValue::Nil),
330 Value::Bool(b) => Ok(SharedValue::Bool(*b)),
331 Value::Long(n) => Ok(SharedValue::Long(*n)),
332 Value::Double(d) => Ok(SharedValue::Double(*d)),
333 Value::Char(c) => Ok(SharedValue::Char(*c)),
334 Value::Uuid(u) => Ok(SharedValue::Uuid(u.get().0)),
335 Value::Str(s) => Ok(SharedValue::Str(Arc::from(s.get().as_str()))),
336 Value::Keyword(kw) => {
337 let kw = kw.get();
338 let ptr = intern_keyword(kw.namespace.as_deref(), &kw.name);
339 Ok(SharedValue::Keyword(ptr))
340 }
341 Value::Symbol(sym) => {
342 let sym = sym.get();
343 let ptr = intern_symbol(sym.namespace.as_deref(), &sym.name, sym.version.as_deref());
344 Ok(SharedValue::Symbol(ptr))
345 }
346 Value::ByteArray(arr) => {
347 let bytes = arr.get().lock().unwrap();
348 let blob = ByteBlob::try_with(bytes.len(), || bytes.iter().map(|&b| b as u8).collect())
350 .map_err(|e| PromoteError::memory_limit("byte-array", e))?;
351 Ok(SharedValue::ByteBlob(Arc::new(blob)))
352 }
353 Value::ByteBlob(blob) => Ok(SharedValue::ByteBlob(blob.clone())),
354 other => Err(PromoteError::not_promotable(other.type_name())),
355 }
356}
357
358pub fn demote(sv: &SharedValue) -> Value {
365 match sv {
366 SharedValue::Nil => Value::Nil,
367 SharedValue::Bool(b) => Value::Bool(*b),
368 SharedValue::Long(n) => Value::Long(*n),
369 SharedValue::Double(d) => Value::Double(*d),
370 SharedValue::Char(c) => Value::Char(*c),
371 SharedValue::Uuid(u) => Value::uuid(*u),
372 SharedValue::Str(s) => Value::Str(GcPtr::new(s.as_ref().to_owned())),
373 SharedValue::Keyword(kw) => Value::Keyword(GcPtr::new(kw.get().clone())),
374 SharedValue::Symbol(sym) => Value::Symbol(GcPtr::new(sym.get().clone())),
375 SharedValue::ByteBlob(blob) => Value::ByteBlob(blob.clone()),
376 }
377}
378
379#[derive(Debug)]
395pub struct SharedAtom {
396 pub cell: Arc<ArcSwap<SharedSlot>>,
397 pub meta: Mutex<Option<SharedValue>>,
398}
399
400impl SharedAtom {
401 pub fn new(val: SharedValue) -> Self {
402 Self {
403 cell: Arc::new(ArcSwap::new(Arc::new(SharedSlot::new(val)))),
404 meta: Mutex::new(None),
405 }
406 }
407
408 pub fn try_new(val: SharedValue) -> Result<Self, MemoryLimitExceeded> {
410 Ok(Self {
411 cell: Arc::new(ArcSwap::new(Arc::new(SharedSlot::try_new(val)?))),
412 meta: Mutex::new(None),
413 })
414 }
415
416 pub fn deref_val(&self) -> Arc<SharedSlot> {
418 self.cell.load_full()
419 }
420
421 pub fn reset(&self, val: SharedValue) -> Arc<SharedSlot> {
423 let arc = Arc::new(SharedSlot::new(val));
424 self.cell.store(arc.clone());
425 arc
426 }
427
428 pub fn try_reset(&self, val: SharedValue) -> Result<Arc<SharedSlot>, MemoryLimitExceeded> {
431 let arc = Arc::new(SharedSlot::try_new(val)?);
432 self.cell.store(arc.clone());
433 Ok(arc)
434 }
435
436 pub fn swap<F>(&self, mut f: F) -> Arc<SharedSlot>
439 where
440 F: FnMut(&SharedValue) -> SharedValue,
441 {
442 self.cell.rcu(|old| Arc::new(SharedSlot::new(f(old))))
443 }
444
445 pub fn compare_and_set(&self, current: &Arc<SharedSlot>, new: SharedValue) -> bool {
454 let prev = self
455 .cell
456 .compare_and_swap(current, Arc::new(SharedSlot::new(new)));
457 std::ptr::eq(Arc::as_ptr(current), Arc::as_ptr(&prev))
459 }
460
461 pub fn try_compare_and_set(
464 &self,
465 current: &Arc<SharedSlot>,
466 new: SharedValue,
467 ) -> Result<bool, MemoryLimitExceeded> {
468 let slot = Arc::new(SharedSlot::try_new(new)?);
469 let prev = self.cell.compare_and_swap(current, slot);
470 Ok(std::ptr::eq(Arc::as_ptr(current), Arc::as_ptr(&prev)))
471 }
472}
473
474#[cfg(test)]
477mod tests {
478 use std::sync::Arc;
479
480 use crate::keyword::Keyword;
481 use crate::value::Value;
482 use cljrs_gc::GcPtr;
483
484 use super::*;
485
486 fn kw(name: &str) -> Value {
487 Value::Keyword(GcPtr::new(Keyword::simple(name)))
488 }
489
490 #[test]
491 fn promote_scalars() {
492 assert!(matches!(promote(&Value::Nil), Ok(SharedValue::Nil)));
493 assert!(matches!(
494 promote(&Value::Bool(true)),
495 Ok(SharedValue::Bool(true))
496 ));
497 assert!(matches!(
498 promote(&Value::Long(42)),
499 Ok(SharedValue::Long(42))
500 ));
501 }
502
503 #[test]
504 fn promote_keyword_interns() {
505 let v = kw("foo");
506 let sv = promote(&v).unwrap();
507 let sv2 = promote(&kw("foo")).unwrap();
508 if let (SharedValue::Keyword(a), SharedValue::Keyword(b)) = (sv, sv2) {
509 assert!(
510 cljrs_gc::StaticGcPtr::ptr_eq(&a, &b),
511 "same keyword should intern to same StaticGcPtr"
512 );
513 } else {
514 panic!("expected SharedValue::Keyword");
515 }
516 }
517
518 #[test]
519 fn demote_roundtrip_long() {
520 let sv = SharedValue::Long(99);
521 assert!(matches!(demote(&sv), Value::Long(99)));
522 }
523
524 #[test]
525 fn demote_roundtrip_keyword() {
526 let sv = promote(&kw("test")).unwrap();
527 let v = demote(&sv);
528 if let Value::Keyword(kw_ptr) = v {
529 assert_eq!(kw_ptr.get().name.as_ref(), "test");
530 } else {
531 panic!("expected Value::Keyword");
532 }
533 }
534
535 #[test]
536 fn promote_non_promotable_returns_err() {
537 let atom = Value::Atom(GcPtr::new(crate::types::Atom::new(Value::Nil)));
538 assert!(promote(&atom).is_err());
539 }
540
541 #[test]
542 fn promote_byte_blob() {
543 let arr: Vec<i8> = vec![1, 2, 3];
544 let v = Value::ByteArray(GcPtr::new(std::sync::Mutex::new(arr)));
545 let sv = promote(&v).unwrap();
546 assert!(matches!(sv, SharedValue::ByteBlob(_)));
547 }
548
549 #[test]
550 fn shared_atom_reset_and_deref() {
551 let atom = SharedAtom::new(SharedValue::Long(0));
552 atom.reset(SharedValue::Long(42));
553 let val = atom.deref_val();
554 assert!(matches!(val.value(), SharedValue::Long(42)));
555 }
556
557 #[test]
558 fn shared_atom_swap() {
559 let atom = SharedAtom::new(SharedValue::Long(1));
560 atom.swap(|old| {
561 if let SharedValue::Long(n) = old {
562 SharedValue::Long(n + 1)
563 } else {
564 SharedValue::Long(0)
565 }
566 });
567 let val = atom.deref_val();
568 assert!(matches!(val.value(), SharedValue::Long(2)));
569 }
570
571 #[test]
572 fn shared_atom_compare_and_set() {
573 let atom = SharedAtom::new(SharedValue::Long(1));
574 let cur = atom.deref_val();
575 assert!(atom.compare_and_set(&cur, SharedValue::Long(2)));
577 assert!(matches!(atom.deref_val().value(), SharedValue::Long(2)));
578 assert!(!atom.compare_and_set(&cur, SharedValue::Long(99)));
580 assert!(matches!(atom.deref_val().value(), SharedValue::Long(2)));
581 }
582
583 #[test]
584 fn shared_atom_is_send_sync() {
585 fn assert_send_sync<T: Send + Sync>() {}
586 assert_send_sync::<SharedAtom>();
587 assert_send_sync::<Arc<SharedAtom>>();
588 }
589
590 #[test]
591 fn byte_blob_shared_across_clone() {
592 let blob = Arc::new(ByteBlob::new(vec![10u8, 20, 30]));
593 let v1 = Value::ByteBlob(blob.clone());
594 let v2 = Value::ByteBlob(blob.clone());
595 if let (Value::ByteBlob(a), Value::ByteBlob(b)) = (&v1, &v2) {
597 assert!(Arc::ptr_eq(a, b));
598 }
599 }
600
601 #[test]
605 fn byte_blob_holds_one_charge() {
606 let blob = Arc::new(ByteBlob::new(vec![0u8; 4096]));
607 assert!(blob.charged_bytes() >= 4096);
608 let clone = blob.clone();
609 assert_eq!(clone.charged_bytes(), blob.charged_bytes());
610 assert_eq!(&clone[..3], &[0, 0, 0]);
611 }
612
613 #[test]
614 fn shared_slot_charges_string_payload() {
615 let short = SharedSlot::new(SharedValue::Long(1));
616 let long = SharedSlot::new(SharedValue::Str(Arc::from("x".repeat(1000))));
617 assert!(short.charged_bytes() > 0);
618 assert_eq!(long.charged_bytes(), short.charged_bytes() + 1000);
619 }
620
621 #[test]
622 fn shared_value_class_reports_live_charges() {
623 let blob = ByteBlob::new(vec![1u8; 1 << 20]);
624 assert!(governor().class_bytes(MemoryClass::SharedValue) >= blob.charged_bytes());
627 }
628}