1use std::cell::UnsafeCell;
10use std::sync::atomic::{AtomicPtr, AtomicU32, AtomicU64, AtomicUsize, Ordering};
11
12use catallaxy_gc::{GcVTable, Header, Tracer, HEADER_SIZE};
13use num_bigint::BigInt;
14
15use crate::buf::WordBuf;
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)]
72pub struct StrObj {
73 pub header: Header,
74 pub len: usize,
75 pub hash: AtomicU64,
77 pub chars: AtomicU64,
80 data: [u8; 0],
81}
82
83impl StrObj {
84 pub const BASE: usize = std::mem::size_of::<StrObj>();
85 pub const INDEX_MIN: usize = 256;
87 pub const STRIDE: usize = 64;
89 const INDEX_READY: u64 = 1 << 63;
90
91 pub(crate) fn layout(len: usize, chars: Option<usize>) -> (usize, u64) {
94 let word = chars.map_or(0, |c| c as u64 + 1);
95 (Self::size_with(len, word), word)
96 }
97
98 fn index_entries(len: usize, word: u64) -> usize {
100 let word = word & !Self::INDEX_READY;
101 if len < Self::INDEX_MIN || len > u32::MAX as usize || word == 0 || (word - 1) as usize == len {
102 return 0;
103 }
104 ((word - 1) as usize).div_ceil(Self::STRIDE)
105 }
106
107 fn size_with(len: usize, word: u64) -> usize {
108 match Self::index_entries(len, word) {
109 0 => Self::BASE + len,
110 n => Self::index_offset(len) + 4 * n,
111 }
112 }
113
114 pub fn alloc_size(&self) -> usize {
116 Self::size_with(self.len, self.chars.load(Ordering::Relaxed))
117 }
118
119 pub fn as_str(&self) -> &str {
120 unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.data.as_ptr(), self.len)) }
122 }
123 pub fn char_count(&self) -> usize {
124 let c = self.chars.load(Ordering::Relaxed) & !Self::INDEX_READY;
125 if c != 0 {
126 return (c - 1) as usize;
127 }
128 let s = self.as_str();
129 let n = if s.is_ascii() { s.len() } else { s.chars().count() };
130 self.chars.store(n as u64 + 1, Ordering::Relaxed);
132 n
133 }
134 #[inline]
136 pub fn is_ascii(&self) -> bool {
137 self.char_count() == self.len
138 }
139
140 fn index(&self) -> Option<&[AtomicU32]> {
142 let n = Self::index_entries(self.len, self.chars.load(Ordering::Relaxed));
143 if n == 0 {
144 return None;
145 }
146 unsafe {
149 let base = (self as *const StrObj as *const u8).add(Self::index_offset(self.len));
150 Some(std::slice::from_raw_parts(base as *const AtomicU32, n))
151 }
152 }
153
154 fn index_offset(len: usize) -> usize {
155 (Self::BASE + len).next_multiple_of(4)
156 }
157
158 fn ready_index(&self) -> Option<&[AtomicU32]> {
160 let ix = self.index()?;
161 if self.chars.load(Ordering::Acquire) & Self::INDEX_READY == 0 {
162 for (slot, (b, _)) in ix.iter().zip(self.as_str().char_indices().step_by(Self::STRIDE)) {
163 slot.store(b as u32, Ordering::Relaxed);
164 }
165 self.chars.fetch_or(Self::INDEX_READY, Ordering::Release);
166 }
167 Some(ix)
168 }
169
170 pub fn byte_offset(&self, i: usize) -> usize {
174 let n = self.char_count();
175 if n == self.len || i == 0 {
176 return i.min(self.len);
177 }
178 if i >= n {
179 return self.len;
180 }
181 let bytes = self.as_str().as_bytes();
182 let (mut b, mut skip) = match self.ready_index() {
183 Some(ix) => (ix[i / Self::STRIDE].load(Ordering::Relaxed) as usize, i % Self::STRIDE),
184 None => (0, i),
185 };
186 while skip > 0 {
187 b += 1;
188 while bytes[b] & 0xC0 == 0x80 {
189 b += 1;
190 }
191 skip -= 1;
192 }
193 b
194 }
195
196 pub fn char_offset(&self, b: usize) -> usize {
198 if self.is_ascii() {
199 return b;
200 }
201 let s = self.as_str();
202 let (c0, b0) = match self.ready_index() {
203 Some(ix) => {
204 let k = ix.partition_point(|x| x.load(Ordering::Relaxed) as usize <= b).max(1) - 1;
206 (k * Self::STRIDE, ix[k].load(Ordering::Relaxed) as usize)
207 }
208 None => (0, 0),
209 };
210 c0 + s[b0..b].chars().count()
211 }
212
213 pub fn char_at(&self, i: usize) -> &str {
215 let b = self.byte_offset(i);
216 let s = self.as_str();
217 let w = if self.is_ascii() { 1 } else { s[b..].chars().next().map_or(0, char::len_utf8) };
218 &s[b..b + w]
219 }
220
221 pub fn char_slice(&self, start: usize, end: usize) -> &str {
223 let n = self.char_count();
224 let (start, end) = (start.min(n), end.min(n));
225 if end <= start {
226 return "";
227 }
228 let a = self.byte_offset(start);
229 &self.as_str()[a..self.byte_offset(end)]
230 }
231
232 pub fn hash_code(&self) -> u64 {
233 let h = self.hash.load(Ordering::Relaxed);
234 if h != 0 {
235 return h;
236 }
237 let mut h = crate::ops::hash_bytes(self.as_str().as_bytes());
238 if h == 0 {
239 h = 1;
240 }
241 self.hash.store(h, Ordering::Relaxed);
242 h
243 }
244}
245
246#[repr(C)]
247pub struct BigIntObj {
248 pub header: Header,
249 pub value: BigInt,
250}
251
252#[repr(C)]
268pub struct ListObj {
269 pub header: Header,
270 buf: AtomicPtr<WordBuf>,
271 len: AtomicUsize,
274 inline: WordBuf,
277}
278
279const _: () = assert!(std::mem::offset_of!(ListObj, inline) == std::mem::offset_of!(ListObj, len) + 8);
280
281const _: () = assert!(std::mem::size_of::<ListObj>() % 16 == 8 && ListObj::INLINE_MAX % 2 == 1);
284
285impl ListObj {
286 pub const INLINE_MAX: usize = 7;
289 pub const INLINE_MIN: usize = 3;
291
292 #[inline]
296 pub fn inline_cap_for(cap: usize) -> usize {
297 if cap > Self::INLINE_MAX {
298 0
299 } else {
300 cap.max(Self::INLINE_MIN) | 1
301 }
302 }
303
304 #[inline]
306 pub fn size_for(icap: usize) -> usize {
307 std::mem::size_of::<ListObj>() + 8 * icap
308 }
309
310 pub(crate) unsafe fn init(p: *mut ListObj, items: Vec<Value>, icap: usize) {
314 if let Err(layout) = Self::try_init(p, items, icap) {
315 std::alloc::handle_alloc_error(layout);
316 }
317 }
318
319 pub(crate) unsafe fn try_init(p: *mut ListObj, items: Vec<Value>, icap: usize) -> Result<(), std::alloc::Layout> {
325 let len = items.len();
326 if icap >= len && icap > 0 {
327 Self::init_inline(p, items.iter().map(|v| v.0), icap, len);
329 return Ok(());
330 }
331 Self::init_inline(p, std::iter::empty(), 0, 0);
332 let mut items = std::mem::ManuallyDrop::new(items);
334 let words = Vec::from_raw_parts(items.as_mut_ptr() as *mut u64, len, items.capacity());
335 match WordBuf::try_from_vec(words, Value::NIL.0) {
336 Ok(buf) => Self::init_buf(p, buf, len),
337 Err(layout) => {
338 Self::init_buf(p, WordBuf::empty(), 0);
339 return Err(layout);
340 }
341 }
342 Ok(())
343 }
344
345 pub(crate) unsafe fn init_from_slice(p: *mut ListObj, items: &[Value], cap: usize, icap: usize) {
351 if let Err(layout) = Self::try_init_from_slice(p, items, cap, icap) {
352 std::alloc::handle_alloc_error(layout);
353 }
354 }
355
356 pub(crate) unsafe fn try_init_from_slice(
362 p: *mut ListObj,
363 items: &[Value],
364 cap: usize,
365 icap: usize,
366 ) -> Result<(), std::alloc::Layout> {
367 let cap = cap.max(items.len());
368 if icap >= cap && icap > 0 {
369 Self::init_inline(p, items.iter().map(|v| v.0), icap, items.len());
370 return Ok(());
371 }
372 Self::init_inline(p, std::iter::empty(), 0, 0);
373 match WordBuf::try_alloc_from(cap, items.iter().map(|v| v.0), Value::NIL.0) {
374 Ok(buf) => Self::init_buf(p, buf, items.len()),
375 Err(layout) => {
376 Self::init_buf(p, WordBuf::empty(), 0);
377 return Err(layout);
378 }
379 }
380 Ok(())
381 }
382
383 unsafe fn init_inline(p: *mut ListObj, items: impl IntoIterator<Item = u64>, icap: usize, len: usize) {
386 let inline = std::ptr::addr_of_mut!((*p).inline);
387 WordBuf::init_in_place(inline, icap, items, Value::NIL.0);
388 if icap > 0 {
389 Self::init_buf(p, inline, len);
390 }
391 }
392
393 unsafe fn init_buf(p: *mut ListObj, buf: *mut WordBuf, len: usize) {
394 std::ptr::addr_of_mut!((*p).len).write(AtomicUsize::new(len));
395 std::ptr::addr_of_mut!((*p).buf).write(AtomicPtr::new(buf));
396 }
397
398 #[inline(always)]
400 fn inline_addr(&self) -> *mut WordBuf {
401 std::ptr::addr_of!(self.inline) as *mut WordBuf
402 }
403
404 pub fn is_inline(&self) -> bool {
406 self.buf.load(Ordering::Acquire) == self.inline_addr()
407 }
408
409 #[inline(always)]
410 pub fn len(&self) -> usize {
411 self.len.load(Ordering::Acquire)
412 }
413
414 #[inline(always)]
415 pub fn is_empty(&self) -> bool {
416 self.len() == 0
417 }
418
419 #[inline(always)]
420 fn slots(&self) -> &[AtomicU64] {
421 unsafe { WordBuf::slots(self.buf.load(Ordering::Acquire)) }
422 }
423
424 #[inline(always)]
427 pub fn get(&self, i: usize) -> Option<Value> {
428 if i >= self.len() {
429 return None;
430 }
431 self.slots().get(i).map(|x| Value(x.load(Ordering::Acquire)))
432 }
433
434 #[inline(always)]
436 pub fn get_local(&self, i: usize) -> Option<Value> {
437 if i >= self.len.load(Ordering::Relaxed) {
438 return None;
439 }
440 let slots = unsafe { WordBuf::slots(self.buf.load(Ordering::Relaxed)) };
441 slots.get(i).map(|x| Value(x.load(Ordering::Relaxed)))
442 }
443
444 pub fn to_vec(&self) -> Vec<Value> {
446 let n = self.len();
447 let slots = self.slots();
448 slots[..n.min(slots.len())].iter().map(|x| Value(x.load(Ordering::Acquire))).collect()
449 }
450
451 pub fn try_to_vec(&self) -> crate::RtResult<Vec<Value>> {
453 let n = self.len();
454 let slots = self.slots();
455 let items = &slots[..n.min(slots.len())];
456 let mut v = crate::try_vec(items.len())?;
457 v.extend(items.iter().map(|x| Value(x.load(Ordering::Acquire))));
458 Ok(v)
459 }
460
461 pub fn capacity(&self) -> usize {
462 unsafe { WordBuf::cap(self.buf.load(Ordering::Acquire)) }
463 }
464
465 pub unsafe fn set(&self, i: usize, v: Value) -> bool {
472 if i >= self.len.load(Ordering::Relaxed) {
473 return false;
474 }
475 let buf = self.buf.load(Ordering::Relaxed);
476 WordBuf::slots(buf)[i].store(v.0, Ordering::Release);
477 self.note_store(buf, i, v);
478 true
479 }
480
481 #[doc(hidden)]
484 pub fn written_range(&self) -> std::ops::Range<usize> {
485 let buf = self.buf.load(Ordering::Acquire);
486 if buf == self.inline_addr() {
487 return 0..self.len();
488 }
489 unsafe { WordBuf::written(buf) }
491 }
492
493 #[inline(always)]
503 unsafe fn note_written(&self, buf: *mut WordBuf, lo: usize, hi: usize) {
504 if !self.header.is_young() && !WordBuf::range_covers(WordBuf::range_word(buf), lo, hi) {
505 self.widen_written(buf, lo, hi);
506 }
507 }
508
509 #[inline(always)]
513 unsafe fn note_store(&self, buf: *mut WordBuf, i: usize, v: Value) {
514 if v.header().is_some_and(|h| h.is_young())
515 && !self.header.is_young()
516 && !WordBuf::range_covers(WordBuf::range_word(buf), i, i + 1)
517 {
518 self.widen_written(buf, i, i + 1);
519 }
520 }
521
522 #[cold]
523 #[inline(never)]
524 unsafe fn widen_written(&self, buf: *mut WordBuf, lo: usize, hi: usize) {
525 if buf != self.inline_addr() {
526 WordBuf::widen_written(buf, lo, hi);
527 }
528 }
529
530 pub unsafe fn push(&self, v: Value, access: Access) {
533 if let Err(layout) = self.try_push(v, access) {
534 std::alloc::handle_alloc_error(layout);
535 }
536 }
537
538 pub unsafe fn try_push(&self, v: Value, access: Access) -> Result<(), std::alloc::Layout> {
544 let n = self.len.load(Ordering::Relaxed);
545 let mut buf = self.buf.load(Ordering::Relaxed);
546 if n == WordBuf::cap(buf) {
547 self.grow(n + 1, access)?;
548 buf = self.buf.load(Ordering::Relaxed);
549 }
550 WordBuf::slots(buf)[n].store(v.0, Ordering::Release);
551 self.note_store(buf, n, v);
552 self.len.store(n + 1, Ordering::Release);
553 Ok(())
554 }
555
556 pub unsafe fn pop(&self) -> Option<Value> {
559 let n = self.len.load(Ordering::Relaxed);
560 if n == 0 {
561 return None;
562 }
563 let slot = &self.slots()[n - 1];
564 let v = Value(slot.load(Ordering::Relaxed));
565 self.len.store(n - 1, Ordering::Release);
566 slot.store(Value::NIL.0, Ordering::Release);
567 Some(v)
568 }
569
570 pub unsafe fn replace(&self, items: &[Value], access: Access) {
575 if let Err(layout) = self.try_replace(items, access) {
576 std::alloc::handle_alloc_error(layout);
577 }
578 }
579
580 pub unsafe fn try_replace(&self, items: &[Value], access: Access) -> Result<(), std::alloc::Layout> {
586 let old_len = self.len.load(Ordering::Relaxed);
587 let n = items.len();
588 if n > self.capacity() {
589 let buf = WordBuf::try_alloc_from(n.max(4), items.iter().map(|v| v.0), Value::NIL.0)?;
591 self.swap_buf(buf, access);
592 self.len.store(n, Ordering::Release);
593 return Ok(());
594 }
595 let buf = self.buf.load(Ordering::Relaxed);
596 if n > 0 {
597 self.note_written(buf, 0, n);
598 }
599 let slots = WordBuf::slots(buf);
600 if n < old_len {
601 self.len.store(n, Ordering::Release);
602 }
603 for (s, v) in slots.iter().zip(items) {
604 s.store(v.0, Ordering::Release);
605 }
606 for s in &slots[n..old_len.max(n)] {
607 s.store(Value::NIL.0, Ordering::Release);
608 }
609 if n >= old_len {
610 self.len.store(n, Ordering::Release);
611 }
612 Ok(())
613 }
614
615 pub unsafe fn modify<R>(&self, access: Access, f: impl FnOnce(&mut Vec<Value>) -> R) -> R {
620 let mut items = self.to_vec();
621 let r = f(&mut items);
622 self.replace(&items, access);
623 r
624 }
625
626 pub unsafe fn try_modify<R>(
632 &self,
633 access: Access,
634 f: impl FnOnce(&mut Vec<Value>) -> R,
635 ) -> Result<R, std::alloc::Layout> {
636 let slots = self.slots();
639 let old = &slots[..self.len().min(slots.len())];
640 let mut items = Vec::new();
641 if items.try_reserve_exact(old.len() + 1).is_err() {
642 return Err(std::alloc::Layout::array::<Value>(old.len() + 1).unwrap_or(std::alloc::Layout::new::<u8>()));
643 }
644 items.extend(old.iter().map(|x| Value(x.load(Ordering::Acquire))));
645 let r = f(&mut items);
646 self.try_replace(&items, access)?;
647 Ok(r)
648 }
649
650 unsafe fn grow(&self, need: usize, access: Access) -> Result<(), std::alloc::Layout> {
653 let n = self.len.load(Ordering::Relaxed);
654 let mut cap = need.max(self.capacity() * 2).max(4);
655 if self.is_inline() {
656 cap = cap.next_power_of_two();
659 }
660 let old_buf = self.buf.load(Ordering::Relaxed);
661 let old = &WordBuf::slots(old_buf)[..n];
662 let buf = WordBuf::try_alloc_from(cap, old.iter().map(|s| s.load(Ordering::Relaxed)), Value::NIL.0)?;
663 if old_buf != self.inline_addr() {
666 WordBuf::copy_written(old_buf, buf);
667 }
668 self.swap_buf(buf, access);
669 Ok(())
670 }
671
672 unsafe fn swap_buf(&self, buf: *mut WordBuf, access: Access) {
673 let old = self.buf.swap(buf, Ordering::AcqRel);
674 if old != self.inline_addr() {
677 crate::buf::release(old, access);
678 }
679 }
680}
681
682#[repr(C)]
683pub struct DictObj {
684 pub header: Header,
685 pub table: DictTable,
686}
687
688impl DictObj {
689 #[inline(always)]
691 pub fn table(&self) -> &DictTable {
692 &self.table
693 }
694}
695
696#[repr(C)]
697pub struct TupleObj {
698 pub header: Header,
699 pub len: usize,
700 elems: [Value; 0],
701}
702
703impl TupleObj {
704 pub fn elems(&self) -> &[Value] {
705 unsafe { std::slice::from_raw_parts(self.elems.as_ptr(), self.len) }
706 }
707 pub(crate) fn elems_mut(&mut self) -> *mut Value {
708 self.elems.as_mut_ptr()
709 }
710 pub fn size_for(n: usize) -> usize {
711 std::mem::size_of::<TupleObj>() + 8 * n
712 }
713}
714
715#[repr(C)]
716pub struct ClosureObj {
717 pub header: Header,
718 pub func: u32,
719 pub ncaps: u32,
720 caps: [Value; 0],
721}
722
723impl ClosureObj {
724 pub fn caps(&self) -> &[Value] {
725 unsafe { std::slice::from_raw_parts(self.caps.as_ptr(), self.ncaps as usize) }
726 }
727 pub(crate) fn caps_mut(&mut self) -> *mut Value {
728 self.caps.as_mut_ptr()
729 }
730 pub fn size_for(n: usize) -> usize {
731 std::mem::size_of::<ClosureObj>() + 8 * n
732 }
733}
734
735#[repr(C)]
736pub struct CellObj {
737 pub header: Header,
738 pub value: AtomicU64,
739}
740
741#[repr(C)]
742pub struct StringBuilderObj {
743 pub header: Header,
744 pub buf: UnsafeCell<String>,
745}
746
747#[repr(C)]
748pub struct MutexObj {
749 pub header: Header,
750 pub raw: parking_lot::ReentrantMutex<()>,
751 pub value: AtomicU64,
752}
753
754#[repr(C)]
755pub struct AtomicObj {
756 pub header: Header,
757 pub value: AtomicU64,
758}
759
760pub struct ChannelInner {
761 pub queue: parking_lot::Mutex<ChannelState>,
762 pub not_empty: parking_lot::Condvar,
763 pub not_full: parking_lot::Condvar,
764 pub capacity: usize,
765 pub waiters: parking_lot::Mutex<Vec<Wake>>,
768}
769
770pub type Wake = std::sync::Arc<dyn Fn() + Send + Sync>;
772
773impl ChannelInner {
774 pub fn wake_fibers(&self) {
776 let ws = std::mem::take(&mut *self.waiters.lock());
777 for w in ws {
778 w();
779 }
780 }
781}
782
783pub struct ChannelState {
784 pub items: std::collections::VecDeque<Value>,
785 pub closed: bool,
786}
787
788#[repr(C)]
789pub struct ChannelObj {
790 pub header: Header,
791 pub inner: Box<ChannelInner>,
792}
793
794pub enum ThreadOutcome {
796 Running,
797 Done(Value),
798 Panicked(String),
799 Joined,
800}
801
802pub struct ThreadInner {
803 pub outcome: parking_lot::Mutex<ThreadOutcome>,
804 pub done: parking_lot::Condvar,
805 pub handle: parking_lot::Mutex<Option<std::thread::JoinHandle<()>>>,
806 pub tid: AtomicU64,
808}
809
810#[repr(C)]
811pub struct ThreadObj {
812 pub header: Header,
813 pub inner: std::sync::Arc<ThreadInner>,
814}
815
816pub trait NativeData: std::any::Any + Send + Sync {
817 fn as_any(&self) -> &dyn std::any::Any;
818}
819impl<T: std::any::Any + Send + Sync> NativeData for T {
820 fn as_any(&self) -> &dyn std::any::Any {
821 self
822 }
823}
824
825#[repr(C)]
826pub struct NativeObj {
827 pub header: Header,
828 pub data: Box<dyn NativeData>,
829}
830
831unsafe fn trace_nothing(_: *const Header, _: &mut Tracer) {}
835
836unsafe fn trace_instance(h: *const Header, t: &mut Tracer) {
837 let o = &*(h as *const InstanceObj);
838 for i in 0..heap_class(h).nfields() {
839 mark_value(t, o.get(i));
840 }
841}
842unsafe fn size_instance(h: *const Header) -> usize {
843 InstanceObj::size_for(heap_class(h).nfields())
844}
845
846unsafe fn size_str(h: *const Header) -> usize {
847 (*(h as *const StrObj)).alloc_size()
848}
849
850unsafe fn fin_bigint(h: *mut Header) {
851 std::ptr::drop_in_place(&mut (*(h as *mut BigIntObj)).value);
852}
853unsafe fn size_bigint(h: *const Header) -> usize {
854 std::mem::size_of::<BigIntObj>() + (*(h as *const BigIntObj)).value.bits() as usize / 8
855}
856
857unsafe fn trace_list(h: *const Header, t: &mut Tracer) {
858 let l = &*(h as *const ListObj);
861 let slots = WordBuf::slots(l.buf.load(Ordering::Relaxed));
862 for s in &slots[..l.len.load(Ordering::Relaxed).min(slots.len())] {
863 mark_value(t, Value(s.load(Ordering::Relaxed)));
864 }
865}
866unsafe fn trace_list_written(h: *const Header, t: &mut Tracer) {
869 let l = &*(h as *const ListObj);
870 let buf = l.buf.load(Ordering::Relaxed);
871 if buf == l.inline_addr() {
872 return trace_list(h, t);
873 }
874 let slots = WordBuf::slots(buf);
875 let r = WordBuf::take_written(buf);
876 let n = l.len.load(Ordering::Relaxed).min(r.end);
877 for s in &slots[r.start.min(n)..n] {
878 mark_value(t, Value(s.load(Ordering::Relaxed)));
879 }
880}
881unsafe fn fin_list(h: *mut Header) {
882 let l = &*(h as *const ListObj);
883 let buf = l.buf.load(Ordering::Relaxed);
884 if buf != l.inline_addr() {
885 WordBuf::free(buf as *mut u8);
886 }
887}
888unsafe fn size_list(h: *const Header) -> usize {
889 let l = &*(h as *const ListObj);
890 let out = if l.is_inline() { 0 } else { l.capacity() };
891 ListObj::size_for(WordBuf::cap(&l.inline)) + 8 * out
892}
893
894unsafe fn trace_dict(h: *const Header, t: &mut Tracer) {
895 (*(h as *const DictObj)).table.trace(t);
896}
897unsafe fn trace_dict_written(h: *const Header, t: &mut Tracer) {
898 (*(h as *const DictObj)).table.trace_written(t);
899}
900unsafe fn fin_dict(h: *mut Header) {
901 std::ptr::drop_in_place(&mut (*(h as *mut DictObj)).table);
902}
903unsafe fn size_dict(h: *const Header) -> usize {
904 std::mem::size_of::<DictObj>() + (*(h as *const DictObj)).table.heap_bytes()
905}
906
907unsafe fn trace_tuple(h: *const Header, t: &mut Tracer) {
908 for &v in (*(h as *const TupleObj)).elems() {
909 mark_value(t, v);
910 }
911}
912unsafe fn size_tuple(h: *const Header) -> usize {
913 TupleObj::size_for((*(h as *const TupleObj)).len)
914}
915
916unsafe fn trace_closure(h: *const Header, t: &mut Tracer) {
917 for &v in (*(h as *const ClosureObj)).caps() {
918 mark_value(t, v);
919 }
920}
921unsafe fn size_closure(h: *const Header) -> usize {
922 ClosureObj::size_for((*(h as *const ClosureObj)).ncaps as usize)
923}
924
925unsafe fn trace_cell(h: *const Header, t: &mut Tracer) {
926 mark_value(t, Value((*(h as *const CellObj)).value.load(Ordering::Relaxed)));
927}
928unsafe fn size_cell(_: *const Header) -> usize {
929 std::mem::size_of::<CellObj>()
930}
931
932unsafe fn fin_sb(h: *mut Header) {
933 std::ptr::drop_in_place((*(h as *mut StringBuilderObj)).buf.get());
934}
935unsafe fn size_sb(h: *const Header) -> usize {
936 std::mem::size_of::<StringBuilderObj>() + (*(*(h as *const StringBuilderObj)).buf.get()).capacity()
937}
938
939unsafe fn trace_mutex(h: *const Header, t: &mut Tracer) {
940 mark_value(t, Value((*(h as *const MutexObj)).value.load(Ordering::Relaxed)));
941}
942unsafe fn fin_mutex(h: *mut Header) {
943 std::ptr::drop_in_place(&mut (*(h as *mut MutexObj)).raw);
944}
945unsafe fn size_mutex(_: *const Header) -> usize {
946 std::mem::size_of::<MutexObj>()
947}
948
949unsafe fn trace_atomic(h: *const Header, t: &mut Tracer) {
950 mark_value(t, Value((*(h as *const AtomicObj)).value.load(Ordering::Relaxed)));
951}
952unsafe fn size_atomic(_: *const Header) -> usize {
953 std::mem::size_of::<AtomicObj>()
954}
955
956unsafe fn trace_channel(h: *const Header, t: &mut Tracer) {
957 let inner = &(*(h as *const ChannelObj)).inner;
959 let q = inner.queue.data_ptr();
960 for &v in (*q).items.iter() {
961 mark_value(t, v);
962 }
963}
964unsafe fn fin_channel(h: *mut Header) {
965 std::ptr::drop_in_place(&mut (*(h as *mut ChannelObj)).inner);
966}
967unsafe fn size_channel(h: *const Header) -> usize {
968 std::mem::size_of::<ChannelObj>() + 8 * (*(*(h as *const ChannelObj)).inner.queue.data_ptr()).items.capacity()
969}
970
971unsafe fn trace_thread(h: *const Header, t: &mut Tracer) {
972 let inner = &(*(h as *const ThreadObj)).inner;
973 if let ThreadOutcome::Done(v) = &*inner.outcome.data_ptr() {
974 mark_value(t, *v);
975 }
976}
977unsafe fn fin_thread(h: *mut Header) {
978 std::ptr::drop_in_place(&mut (*(h as *mut ThreadObj)).inner);
979}
980unsafe fn size_thread(_: *const Header) -> usize {
981 std::mem::size_of::<ThreadObj>() + 128
982}
983
984unsafe fn fin_native(h: *mut Header) {
985 std::ptr::drop_in_place(&mut (*(h as *mut NativeObj)).data);
986}
987unsafe fn size_native(_: *const Header) -> usize {
988 std::mem::size_of::<NativeObj>()
989}
990
991unsafe fn size_zero(_: *const Header) -> usize {
992 0
993}
994
995#[allow(clippy::type_complexity)]
996pub(crate) fn vtable_for(kind: ObjKind) -> GcVTable {
997 let (trace, finalize, size): (
998 unsafe fn(*const Header, &mut Tracer),
999 Option<unsafe fn(*mut Header)>,
1000 unsafe fn(*const Header) -> usize,
1001 ) = match kind {
1002 ObjKind::Immediate => (trace_nothing, None, size_zero),
1003 ObjKind::Instance => (trace_instance, None, size_instance),
1004 ObjKind::Str => (trace_nothing, None, size_str),
1005 ObjKind::BigInt => (trace_nothing, Some(fin_bigint), size_bigint),
1006 ObjKind::List => (trace_list, Some(fin_list), size_list),
1007 ObjKind::Dict | ObjKind::Set => (trace_dict, Some(fin_dict), size_dict),
1008 ObjKind::Tuple => (trace_tuple, None, size_tuple),
1009 ObjKind::Closure => (trace_closure, None, size_closure),
1010 ObjKind::Cell => (trace_cell, None, size_cell),
1011 ObjKind::StringBuilder => (trace_nothing, Some(fin_sb), size_sb),
1012 ObjKind::Mutex => (trace_mutex, Some(fin_mutex), size_mutex),
1013 ObjKind::Atomic => (trace_atomic, None, size_atomic),
1014 ObjKind::Channel => (trace_channel, Some(fin_channel), size_channel),
1015 ObjKind::Thread => (trace_thread, Some(fin_thread), size_thread),
1016 ObjKind::Native => (trace_nothing, Some(fin_native), size_native),
1017 };
1018 let trace_remembered: Option<unsafe fn(*const Header, &mut Tracer)> = match kind {
1019 ObjKind::List => Some(trace_list_written),
1020 ObjKind::Dict | ObjKind::Set => Some(trace_dict_written),
1021 _ => None,
1022 };
1023 GcVTable { trace, finalize, size, trace_remembered }
1024}
1025
1026macro_rules! view {
1030 ($fn:ident, $ty:ty) => {
1031 #[inline(always)]
1034 pub unsafe fn $fn<'a>(v: Value) -> &'a $ty {
1035 &*(v.as_ptr() as *const $ty)
1036 }
1037 };
1038}
1039view!(as_instance, InstanceObj);
1040view!(as_str_obj, StrObj);
1041view!(as_bigint, BigIntObj);
1042view!(as_list, ListObj);
1043view!(as_dict, DictObj);
1044view!(as_tuple, TupleObj);
1045view!(as_closure, ClosureObj);
1046view!(as_cell, CellObj);
1047view!(as_sb, StringBuilderObj);
1048view!(as_mutex, MutexObj);
1049view!(as_atomic, AtomicObj);
1050view!(as_channel, ChannelObj);
1051view!(as_thread, ThreadObj);
1052view!(as_native, NativeObj);
1053
1054#[inline]
1056pub fn kind_of(v: Value) -> ObjKind {
1057 if v.is_ptr() {
1058 unsafe { heap_class(v.as_ptr()).kind }
1059 } else {
1060 ObjKind::Immediate
1061 }
1062}
1063
1064#[inline]
1066pub fn str_of<'a>(v: Value) -> Option<&'a str> {
1067 if kind_of(v) == ObjKind::Str {
1068 Some(unsafe { as_str_obj(v) }.as_str())
1069 } else {
1070 None
1071 }
1072}