1use std::alloc::Layout;
10use std::cell::UnsafeCell;
11use std::sync::atomic::{AtomicPtr, AtomicU64, AtomicUsize, Ordering};
12
13use catallaxy_gc::{GcVTable, Header, Tracer, HEADER_SIZE};
14use num_bigint::BigInt;
15
16use crate::class::{heap_class, ObjKind};
17use crate::dict::DictTable;
18use crate::ownership::Access;
19use crate::value::Value;
20
21#[inline(always)]
22pub fn mark_value(t: &mut Tracer, v: Value) {
23 if v.is_ptr() {
24 t.mark(v.as_ptr());
25 }
26}
27
28#[repr(C)]
29pub struct InstanceObj {
30 pub header: Header,
31 fields: [AtomicU64; 0],
32}
33
34impl InstanceObj {
35 #[inline(always)]
36 pub fn field(&self, i: usize) -> &AtomicU64 {
37 unsafe { &*self.fields.as_ptr().add(i) }
39 }
40 #[inline(always)]
41 pub fn get(&self, i: usize) -> Value {
42 Value(self.field(i).load(Ordering::Relaxed))
43 }
44 #[inline(always)]
45 pub fn get_acquire(&self, i: usize) -> Value {
46 Value(self.field(i).load(Ordering::Acquire))
47 }
48 #[inline(always)]
49 pub fn set(&self, i: usize, v: Value) {
50 self.field(i).store(v.0, Ordering::Relaxed)
51 }
52 #[inline(always)]
53 pub fn set_release(&self, i: usize, v: Value) {
54 self.field(i).store(v.0, Ordering::Release)
55 }
56 pub fn nfields(&self) -> usize {
57 unsafe { heap_class(&self.header).nfields() }
58 }
59 pub fn size_for(nfields: usize) -> usize {
60 HEADER_SIZE + 8 * nfields
61 }
62}
63
64#[repr(C)]
65pub struct StrObj {
66 pub header: Header,
67 pub len: usize,
68 pub hash: AtomicU64,
70 pub chars: AtomicU64,
72 data: [u8; 0],
73}
74
75impl StrObj {
76 pub const BASE: usize = std::mem::size_of::<StrObj>();
77 pub fn as_str(&self) -> &str {
78 unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.data.as_ptr(), self.len)) }
80 }
81 pub(crate) fn data_ptr(&mut self) -> *mut u8 {
82 self.data.as_mut_ptr()
83 }
84 pub fn char_count(&self) -> usize {
85 let c = self.chars.load(Ordering::Relaxed);
86 if c != 0 {
87 return (c - 1) as usize;
88 }
89 let s = self.as_str();
90 let n = if s.is_ascii() { s.len() } else { s.chars().count() };
91 self.chars.store(n as u64 + 1, Ordering::Relaxed);
92 n
93 }
94 pub fn hash_code(&self) -> u64 {
95 let h = self.hash.load(Ordering::Relaxed);
96 if h != 0 {
97 return h;
98 }
99 let mut h = crate::ops::hash_bytes(self.as_str().as_bytes());
100 if h == 0 {
101 h = 1;
102 }
103 self.hash.store(h, Ordering::Relaxed);
104 h
105 }
106}
107
108#[repr(C)]
109pub struct BigIntObj {
110 pub header: Header,
111 pub value: BigInt,
112}
113
114#[repr(C)]
118pub struct ListBuf {
119 cap: usize,
120 slots: [AtomicU64; 0],
121}
122
123static EMPTY_LIST_BUF: ListBuf = ListBuf { cap: 0, slots: [] };
125
126impl ListBuf {
127 fn layout(cap: usize) -> Layout {
128 Layout::from_size_align(8 * (cap + 1), 8).expect("list too large")
129 }
130
131 fn alloc(cap: usize) -> *mut ListBuf {
132 if cap == 0 {
133 return &EMPTY_LIST_BUF as *const ListBuf as *mut ListBuf;
134 }
135 unsafe {
136 let p = std::alloc::alloc(Self::layout(cap)) as *mut ListBuf;
137 if p.is_null() {
138 std::alloc::handle_alloc_error(Self::layout(cap));
139 }
140 std::ptr::addr_of_mut!((*p).cap).write(cap);
141 let slots = Self::slots_ptr(p);
142 for i in 0..cap {
143 slots.add(i).write(AtomicU64::new(Value::NIL.0));
144 }
145 p
146 }
147 }
148
149 unsafe fn free(p: *mut u8) {
152 let b = p as *mut ListBuf;
153 let cap = (*b).cap;
154 if cap > 0 {
155 std::alloc::dealloc(p, Self::layout(cap));
156 }
157 }
158
159 #[inline(always)]
160 fn slots_ptr(p: *const ListBuf) -> *mut AtomicU64 {
161 unsafe { (p as *mut u8).add(8) as *mut AtomicU64 }
162 }
163
164 #[inline(always)]
167 unsafe fn slots<'a>(p: *const ListBuf) -> &'a [AtomicU64] {
168 std::slice::from_raw_parts(Self::slots_ptr(p), (*p).cap)
169 }
170}
171
172#[repr(C)]
180pub struct ListObj {
181 pub header: Header,
182 len: AtomicUsize,
183 buf: AtomicPtr<ListBuf>,
184}
185
186impl ListObj {
187 pub(crate) unsafe fn init(p: *mut ListObj, items: Vec<Value>) {
190 let buf = ListBuf::alloc(items.capacity());
191 let slots = ListBuf::slots_ptr(buf);
192 for (i, v) in items.iter().enumerate() {
193 (*slots.add(i)).store(v.0, Ordering::Relaxed);
194 }
195 std::ptr::addr_of_mut!((*p).len).write(AtomicUsize::new(items.len()));
196 std::ptr::addr_of_mut!((*p).buf).write(AtomicPtr::new(buf));
197 }
198
199 #[inline(always)]
200 pub fn len(&self) -> usize {
201 self.len.load(Ordering::Acquire)
202 }
203
204 #[inline(always)]
205 pub fn is_empty(&self) -> bool {
206 self.len() == 0
207 }
208
209 #[inline(always)]
210 fn slots(&self) -> &[AtomicU64] {
211 unsafe { ListBuf::slots(self.buf.load(Ordering::Acquire)) }
212 }
213
214 #[inline(always)]
217 pub fn get(&self, i: usize) -> Option<Value> {
218 if i >= self.len() {
219 return None;
220 }
221 self.slots().get(i).map(|x| Value(x.load(Ordering::Acquire)))
222 }
223
224 #[inline(always)]
226 pub fn get_local(&self, i: usize) -> Option<Value> {
227 if i >= self.len.load(Ordering::Relaxed) {
228 return None;
229 }
230 let slots = unsafe { ListBuf::slots(self.buf.load(Ordering::Relaxed)) };
231 slots.get(i).map(|x| Value(x.load(Ordering::Relaxed)))
232 }
233
234 pub fn to_vec(&self) -> Vec<Value> {
236 let n = self.len();
237 let slots = self.slots();
238 slots[..n.min(slots.len())].iter().map(|x| Value(x.load(Ordering::Acquire))).collect()
239 }
240
241 pub fn capacity(&self) -> usize {
242 unsafe { (*self.buf.load(Ordering::Acquire)).cap }
243 }
244
245 pub unsafe fn set(&self, i: usize, v: Value) -> bool {
252 if i >= self.len.load(Ordering::Relaxed) {
253 return false;
254 }
255 self.slots()[i].store(v.0, Ordering::Release);
256 true
257 }
258
259 pub unsafe fn push(&self, v: Value, access: Access) {
262 let n = self.len.load(Ordering::Relaxed);
263 if n == self.capacity() {
264 self.grow(n + 1, access);
265 }
266 self.slots()[n].store(v.0, Ordering::Release);
267 self.len.store(n + 1, Ordering::Release);
268 }
269
270 pub unsafe fn pop(&self) -> Option<Value> {
273 let n = self.len.load(Ordering::Relaxed);
274 if n == 0 {
275 return None;
276 }
277 let slot = &self.slots()[n - 1];
278 let v = Value(slot.load(Ordering::Relaxed));
279 self.len.store(n - 1, Ordering::Release);
280 slot.store(Value::NIL.0, Ordering::Release);
281 Some(v)
282 }
283
284 pub unsafe fn replace(&self, items: &[Value], access: Access) {
289 let old_len = self.len.load(Ordering::Relaxed);
290 let n = items.len();
291 if n > self.capacity() {
292 let buf = ListBuf::alloc(n.max(4));
293 let slots = ListBuf::slots(buf);
294 for (s, v) in slots.iter().zip(items) {
295 s.store(v.0, Ordering::Relaxed);
296 }
297 self.swap_buf(buf, access);
298 self.len.store(n, Ordering::Release);
299 return;
300 }
301 let slots = self.slots();
302 if n < old_len {
303 self.len.store(n, Ordering::Release);
304 }
305 for (s, v) in slots.iter().zip(items) {
306 s.store(v.0, Ordering::Release);
307 }
308 for s in &slots[n..old_len.max(n)] {
309 s.store(Value::NIL.0, Ordering::Release);
310 }
311 if n >= old_len {
312 self.len.store(n, Ordering::Release);
313 }
314 }
315
316 pub unsafe fn modify<R>(&self, access: Access, f: impl FnOnce(&mut Vec<Value>) -> R) -> R {
321 let mut items = self.to_vec();
322 let r = f(&mut items);
323 self.replace(&items, access);
324 r
325 }
326
327 unsafe fn grow(&self, need: usize, access: Access) {
328 let n = self.len.load(Ordering::Relaxed);
329 let cap = need.max(self.capacity() * 2).max(4);
330 let buf = ListBuf::alloc(cap);
331 let new = ListBuf::slots(buf);
332 for (d, s) in new.iter().zip(&self.slots()[..n]) {
333 d.store(s.load(Ordering::Relaxed), Ordering::Relaxed);
334 }
335 self.swap_buf(buf, access);
336 }
337
338 unsafe fn swap_buf(&self, buf: *mut ListBuf, access: Access) {
339 crate::note_external((*buf).cap * 8);
340 let old = self.buf.swap(buf, Ordering::AcqRel);
341 if (*old).cap == 0 {
342 return;
343 }
344 if access == Access::Local && !crate::ownership::PROMOTE_ON_ACCESS.load(Ordering::Relaxed) {
345 ListBuf::free(old as *mut u8);
346 } else {
347 catallaxy_gc::retire(old as *mut u8, ListBuf::free);
348 }
349 }
350}
351
352#[repr(C)]
353pub struct DictObj {
354 pub header: Header,
355 pub table: UnsafeCell<DictTable>,
356}
357
358impl DictObj {
359 #[inline(always)]
362 pub unsafe fn table(&self) -> &DictTable {
363 &*self.table.get()
364 }
365 #[inline(always)]
368 #[allow(clippy::mut_from_ref)]
369 pub unsafe fn table_mut(&self) -> &mut DictTable {
370 &mut *self.table.get()
371 }
372}
373
374#[repr(C)]
375pub struct TupleObj {
376 pub header: Header,
377 pub len: usize,
378 elems: [Value; 0],
379}
380
381impl TupleObj {
382 pub fn elems(&self) -> &[Value] {
383 unsafe { std::slice::from_raw_parts(self.elems.as_ptr(), self.len) }
384 }
385 pub(crate) fn elems_mut(&mut self) -> *mut Value {
386 self.elems.as_mut_ptr()
387 }
388 pub fn size_for(n: usize) -> usize {
389 std::mem::size_of::<TupleObj>() + 8 * n
390 }
391}
392
393#[repr(C)]
394pub struct ClosureObj {
395 pub header: Header,
396 pub func: u32,
397 pub ncaps: u32,
398 caps: [Value; 0],
399}
400
401impl ClosureObj {
402 pub fn caps(&self) -> &[Value] {
403 unsafe { std::slice::from_raw_parts(self.caps.as_ptr(), self.ncaps as usize) }
404 }
405 pub(crate) fn caps_mut(&mut self) -> *mut Value {
406 self.caps.as_mut_ptr()
407 }
408 pub fn size_for(n: usize) -> usize {
409 std::mem::size_of::<ClosureObj>() + 8 * n
410 }
411}
412
413#[repr(C)]
414pub struct CellObj {
415 pub header: Header,
416 pub value: AtomicU64,
417}
418
419#[repr(C)]
420pub struct StringBuilderObj {
421 pub header: Header,
422 pub buf: UnsafeCell<String>,
423}
424
425#[repr(C)]
426pub struct MutexObj {
427 pub header: Header,
428 pub raw: parking_lot::ReentrantMutex<()>,
429 pub value: AtomicU64,
430}
431
432#[repr(C)]
433pub struct AtomicObj {
434 pub header: Header,
435 pub value: AtomicU64,
436}
437
438pub struct ChannelInner {
439 pub queue: parking_lot::Mutex<ChannelState>,
440 pub not_empty: parking_lot::Condvar,
441 pub not_full: parking_lot::Condvar,
442 pub capacity: usize,
443}
444
445pub struct ChannelState {
446 pub items: std::collections::VecDeque<Value>,
447 pub closed: bool,
448}
449
450#[repr(C)]
451pub struct ChannelObj {
452 pub header: Header,
453 pub inner: Box<ChannelInner>,
454}
455
456pub enum ThreadOutcome {
458 Running,
459 Done(Value),
460 Panicked(String),
461 Joined,
462}
463
464pub struct ThreadInner {
465 pub outcome: parking_lot::Mutex<ThreadOutcome>,
466 pub done: parking_lot::Condvar,
467 pub handle: parking_lot::Mutex<Option<std::thread::JoinHandle<()>>>,
468 pub tid: AtomicU64,
470}
471
472#[repr(C)]
473pub struct ThreadObj {
474 pub header: Header,
475 pub inner: std::sync::Arc<ThreadInner>,
476}
477
478pub trait NativeData: std::any::Any + Send + Sync {
479 fn as_any(&self) -> &dyn std::any::Any;
480}
481impl<T: std::any::Any + Send + Sync> NativeData for T {
482 fn as_any(&self) -> &dyn std::any::Any {
483 self
484 }
485}
486
487#[repr(C)]
488pub struct NativeObj {
489 pub header: Header,
490 pub data: Box<dyn NativeData>,
491}
492
493unsafe fn trace_nothing(_: *const Header, _: &mut Tracer) {}
497
498unsafe fn trace_instance(h: *const Header, t: &mut Tracer) {
499 let o = &*(h as *const InstanceObj);
500 for i in 0..heap_class(h).nfields() {
501 mark_value(t, o.get(i));
502 }
503}
504unsafe fn size_instance(h: *const Header) -> usize {
505 InstanceObj::size_for(heap_class(h).nfields())
506}
507
508unsafe fn size_str(h: *const Header) -> usize {
509 StrObj::BASE + (*(h as *const StrObj)).len
510}
511
512unsafe fn fin_bigint(h: *mut Header) {
513 std::ptr::drop_in_place(&mut (*(h as *mut BigIntObj)).value);
514}
515unsafe fn size_bigint(h: *const Header) -> usize {
516 std::mem::size_of::<BigIntObj>() + (*(h as *const BigIntObj)).value.bits() as usize / 8
517}
518
519unsafe fn trace_list(h: *const Header, t: &mut Tracer) {
520 for s in (*(h as *const ListObj)).slots() {
522 mark_value(t, Value(s.load(Ordering::Relaxed)));
523 }
524}
525unsafe fn fin_list(h: *mut Header) {
526 ListBuf::free(*(*(h as *mut ListObj)).buf.get_mut() as *mut u8);
527}
528unsafe fn size_list(h: *const Header) -> usize {
529 std::mem::size_of::<ListObj>() + 8 * (*(h as *const ListObj)).capacity()
530}
531
532unsafe fn trace_dict(h: *const Header, t: &mut Tracer) {
533 (*(*(h as *const DictObj)).table.get()).trace(t);
534}
535unsafe fn fin_dict(h: *mut Header) {
536 std::ptr::drop_in_place((*(h as *mut DictObj)).table.get());
537}
538unsafe fn size_dict(h: *const Header) -> usize {
539 std::mem::size_of::<DictObj>() + (*(*(h as *const DictObj)).table.get()).heap_bytes()
540}
541
542unsafe fn trace_tuple(h: *const Header, t: &mut Tracer) {
543 for &v in (*(h as *const TupleObj)).elems() {
544 mark_value(t, v);
545 }
546}
547unsafe fn size_tuple(h: *const Header) -> usize {
548 TupleObj::size_for((*(h as *const TupleObj)).len)
549}
550
551unsafe fn trace_closure(h: *const Header, t: &mut Tracer) {
552 for &v in (*(h as *const ClosureObj)).caps() {
553 mark_value(t, v);
554 }
555}
556unsafe fn size_closure(h: *const Header) -> usize {
557 ClosureObj::size_for((*(h as *const ClosureObj)).ncaps as usize)
558}
559
560unsafe fn trace_cell(h: *const Header, t: &mut Tracer) {
561 mark_value(t, Value((*(h as *const CellObj)).value.load(Ordering::Relaxed)));
562}
563unsafe fn size_cell(_: *const Header) -> usize {
564 std::mem::size_of::<CellObj>()
565}
566
567unsafe fn fin_sb(h: *mut Header) {
568 std::ptr::drop_in_place((*(h as *mut StringBuilderObj)).buf.get());
569}
570unsafe fn size_sb(h: *const Header) -> usize {
571 std::mem::size_of::<StringBuilderObj>() + (*(*(h as *const StringBuilderObj)).buf.get()).capacity()
572}
573
574unsafe fn trace_mutex(h: *const Header, t: &mut Tracer) {
575 mark_value(t, Value((*(h as *const MutexObj)).value.load(Ordering::Relaxed)));
576}
577unsafe fn fin_mutex(h: *mut Header) {
578 std::ptr::drop_in_place(&mut (*(h as *mut MutexObj)).raw);
579}
580unsafe fn size_mutex(_: *const Header) -> usize {
581 std::mem::size_of::<MutexObj>()
582}
583
584unsafe fn trace_atomic(h: *const Header, t: &mut Tracer) {
585 mark_value(t, Value((*(h as *const AtomicObj)).value.load(Ordering::Relaxed)));
586}
587unsafe fn size_atomic(_: *const Header) -> usize {
588 std::mem::size_of::<AtomicObj>()
589}
590
591unsafe fn trace_channel(h: *const Header, t: &mut Tracer) {
592 let inner = &(*(h as *const ChannelObj)).inner;
594 let q = inner.queue.data_ptr();
595 for &v in (*q).items.iter() {
596 mark_value(t, v);
597 }
598}
599unsafe fn fin_channel(h: *mut Header) {
600 std::ptr::drop_in_place(&mut (*(h as *mut ChannelObj)).inner);
601}
602unsafe fn size_channel(h: *const Header) -> usize {
603 std::mem::size_of::<ChannelObj>() + 8 * (*(*(h as *const ChannelObj)).inner.queue.data_ptr()).items.capacity()
604}
605
606unsafe fn trace_thread(h: *const Header, t: &mut Tracer) {
607 let inner = &(*(h as *const ThreadObj)).inner;
608 if let ThreadOutcome::Done(v) = &*inner.outcome.data_ptr() {
609 mark_value(t, *v);
610 }
611}
612unsafe fn fin_thread(h: *mut Header) {
613 std::ptr::drop_in_place(&mut (*(h as *mut ThreadObj)).inner);
614}
615unsafe fn size_thread(_: *const Header) -> usize {
616 std::mem::size_of::<ThreadObj>() + 128
617}
618
619unsafe fn fin_native(h: *mut Header) {
620 std::ptr::drop_in_place(&mut (*(h as *mut NativeObj)).data);
621}
622unsafe fn size_native(_: *const Header) -> usize {
623 std::mem::size_of::<NativeObj>()
624}
625
626unsafe fn size_zero(_: *const Header) -> usize {
627 0
628}
629
630#[allow(clippy::type_complexity)]
631pub(crate) fn vtable_for(kind: ObjKind) -> GcVTable {
632 let (trace, finalize, size): (
633 unsafe fn(*const Header, &mut Tracer),
634 Option<unsafe fn(*mut Header)>,
635 unsafe fn(*const Header) -> usize,
636 ) = match kind {
637 ObjKind::Immediate => (trace_nothing, None, size_zero),
638 ObjKind::Instance => (trace_instance, None, size_instance),
639 ObjKind::Str => (trace_nothing, None, size_str),
640 ObjKind::BigInt => (trace_nothing, Some(fin_bigint), size_bigint),
641 ObjKind::List => (trace_list, Some(fin_list), size_list),
642 ObjKind::Dict | ObjKind::Set => (trace_dict, Some(fin_dict), size_dict),
643 ObjKind::Tuple => (trace_tuple, None, size_tuple),
644 ObjKind::Closure => (trace_closure, None, size_closure),
645 ObjKind::Cell => (trace_cell, None, size_cell),
646 ObjKind::StringBuilder => (trace_nothing, Some(fin_sb), size_sb),
647 ObjKind::Mutex => (trace_mutex, Some(fin_mutex), size_mutex),
648 ObjKind::Atomic => (trace_atomic, None, size_atomic),
649 ObjKind::Channel => (trace_channel, Some(fin_channel), size_channel),
650 ObjKind::Thread => (trace_thread, Some(fin_thread), size_thread),
651 ObjKind::Native => (trace_nothing, Some(fin_native), size_native),
652 };
653 GcVTable { trace, finalize, size }
654}
655
656macro_rules! view {
660 ($fn:ident, $ty:ty) => {
661 #[inline(always)]
664 pub unsafe fn $fn<'a>(v: Value) -> &'a $ty {
665 &*(v.as_ptr() as *const $ty)
666 }
667 };
668}
669view!(as_instance, InstanceObj);
670view!(as_str_obj, StrObj);
671view!(as_bigint, BigIntObj);
672view!(as_list, ListObj);
673view!(as_dict, DictObj);
674view!(as_tuple, TupleObj);
675view!(as_closure, ClosureObj);
676view!(as_cell, CellObj);
677view!(as_sb, StringBuilderObj);
678view!(as_mutex, MutexObj);
679view!(as_atomic, AtomicObj);
680view!(as_channel, ChannelObj);
681view!(as_thread, ThreadObj);
682view!(as_native, NativeObj);
683
684#[inline]
686pub fn kind_of(v: Value) -> ObjKind {
687 if v.is_ptr() {
688 unsafe { heap_class(v.as_ptr()).kind }
689 } else {
690 ObjKind::Immediate
691 }
692}
693
694#[inline]
696pub fn str_of<'a>(v: Value) -> Option<&'a str> {
697 if kind_of(v) == ObjKind::Str {
698 Some(unsafe { as_str_obj(v) }.as_str())
699 } else {
700 None
701 }
702}