1use std::collections::{HashMap, HashSet};
56use std::sync::{Mutex, OnceLock};
57
58use crate::constraints::randc::RandcState;
59use crate::netlist::ClassTemplateId;
60
61#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
69pub struct FieldSlot {
70 pub offset: usize,
71 pub words: usize,
72}
73
74#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
80pub struct Window {
81 pub bit: u32,
83 pub width: u32,
85 pub field_width: u32,
87}
88
89fn get_bits(words: &[u64], lo: usize, n: usize) -> u64 {
92 debug_assert!(n <= 64);
93 if n == 0 {
94 return 0;
95 }
96 let w = lo / 64;
97 let sh = lo % 64;
98 let a = words.get(w).copied().unwrap_or(0) >> sh;
99 let v = if sh == 0 {
100 a
101 } else {
102 a | (words.get(w + 1).copied().unwrap_or(0) << (64 - sh))
103 };
104 if n == 64 { v } else { v & ((1u64 << n) - 1) }
105}
106
107fn set_bits(words: &mut [u64], lo: usize, n: usize, v: u64) {
110 debug_assert!(n <= 64);
111 if n == 0 {
112 return;
113 }
114 let mask = if n == 64 { u64::MAX } else { (1u64 << n) - 1 };
115 let v = v & mask;
116 let w = lo / 64;
117 let sh = lo % 64;
118 if let Some(x) = words.get_mut(w) {
119 *x = (*x & !(mask << sh)) | (v << sh);
120 }
121 if sh != 0
122 && sh + n > 64
123 && let Some(x) = words.get_mut(w + 1)
124 {
125 let hi_mask = mask >> (64 - sh);
126 *x = (*x & !hi_mask) | (v >> (64 - sh));
127 }
128}
129
130impl Window {
131 fn read(&self, fields: &[u64], val: &mut [u64], unk: &mut [u64]) {
134 for (i, (v, u)) in val.iter_mut().zip(unk.iter_mut()).enumerate() {
135 let lo = i * 64;
136 let n = (self.width as usize).saturating_sub(lo).min(64);
137 *v = get_bits(fields, self.bit as usize + lo, n);
138 *u = get_bits(fields, (self.field_width + self.bit) as usize + lo, n);
139 }
140 }
141
142 fn write(&self, fields: &mut [u64], val: &[u64]) {
144 for i in 0..(self.width as usize).div_ceil(64) {
145 let lo = i * 64;
146 let n = (self.width as usize - lo).min(64);
147 let v = val.get(i).copied().unwrap_or(0);
148 set_bits(fields, self.bit as usize + lo, n, v);
149 set_bits(fields, (self.field_width + self.bit) as usize + lo, n, 0);
150 }
151 }
152}
153
154#[derive(Clone, Copy, PartialEq, Eq, Debug)]
161pub struct NestedHandle {
162 pub slot: FieldSlot,
164 pub bit: u32,
166 pub coll: bool,
168}
169
170fn nested_get(fields: &[u64], nh: &NestedHandle) -> Option<u64> {
173 let w = nh.slot.offset + (nh.bit / 64) as usize;
174 let sh = nh.bit % 64;
175 let lo = *fields.get(w)? >> sh;
176 if sh == 0 {
177 return Some(lo);
178 }
179 Some(lo | (*fields.get(w + 1)? << (64 - sh)))
180}
181
182fn nested_set(fields: &mut [u64], nh: &NestedHandle, h: u64) {
184 let w = nh.slot.offset + (nh.bit / 64) as usize;
185 let sh = nh.bit % 64;
186 if sh == 0 {
187 if let Some(d) = fields.get_mut(w) {
188 *d = h;
189 }
190 return;
191 }
192 if w + 1 >= fields.len() {
193 return;
194 }
195 let lo_mask = u64::MAX << sh;
196 fields[w] = (fields[w] & !lo_mask) | (h << sh);
197 fields[w + 1] = (fields[w + 1] & lo_mask) | (h >> (64 - sh));
198}
199
200#[derive(Clone, Copy, Debug, PartialEq, Eq)]
202pub enum RandKind {
203 Rand,
204 RandC,
205}
206
207#[derive(Clone, Copy, Debug, PartialEq, Eq)]
212pub struct RandProp {
213 pub slot: FieldSlot,
214 pub kind: RandKind,
215}
216
217#[derive(Clone, Debug, Default, PartialEq, Eq)]
226pub struct RandMeta {
227 pub rand_mode: Vec<bool>,
229 pub leaf_off: Vec<(usize, u32)>,
236 pub leaf_on: Vec<(usize, u32)>,
242}
243
244struct ObjEntry {
245 version: u32,
246 template: ClassTemplateId,
247 refcount: u32,
248 fields: Box<[u64]>,
249 prng_seed: u64,
250 rand_meta: RandMeta,
251 randc: HashMap<usize, RandcState>,
257 static_rand_off: HashSet<usize>,
263 constraint_off: HashSet<u32>,
270}
271
272struct Table {
273 entries: Vec<Option<ObjEntry>>,
275 versions: Vec<u32>,
279 free: Vec<u32>,
280}
281
282fn table() -> &'static Mutex<Table> {
283 static T: OnceLock<Mutex<Table>> = OnceLock::new();
284 T.get_or_init(|| {
285 Mutex::new(Table {
286 entries: vec![None],
287 versions: vec![0],
288 free: Vec::new(),
289 })
290 })
291}
292
293#[derive(Default, Clone)]
297struct TemplateInfo {
298 handle_fields: Vec<FieldSlot>,
299 collection_fields: Vec<FieldSlot>,
304 nested_handles: Vec<NestedHandle>,
308 ancestors: HashSet<ClassTemplateId>,
312 rand_props: Vec<RandProp>,
314}
315
316fn registry() -> &'static Mutex<HashMap<ClassTemplateId, TemplateInfo>> {
317 static R: OnceLock<Mutex<HashMap<ClassTemplateId, TemplateInfo>>> = OnceLock::new();
318 R.get_or_init(|| Mutex::new(HashMap::new()))
319}
320
321pub fn register_handle_fields(template: ClassTemplateId, slots: Vec<FieldSlot>) {
328 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
329 r.entry(template).or_default().handle_fields = slots;
330}
331
332pub fn register_collection_fields(template: ClassTemplateId, slots: Vec<FieldSlot>) {
339 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
340 r.entry(template).or_default().collection_fields = slots;
341}
342
343pub fn register_nested_handles(template: ClassTemplateId, nested: Vec<NestedHandle>) {
350 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
351 r.entry(template).or_default().nested_handles = nested;
352}
353
354pub fn register_ancestors(template: ClassTemplateId, mut ancestors: HashSet<ClassTemplateId>) {
361 ancestors.insert(template);
362 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
363 r.entry(template).or_default().ancestors = ancestors;
364}
365
366pub fn register_rand_props(template: ClassTemplateId, props: Vec<RandProp>) {
371 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
372 r.entry(template).or_default().rand_props = props;
373}
374
375#[derive(Clone, Debug, PartialEq, Eq)]
379pub struct TemplateSnapshot {
380 pub id: ClassTemplateId,
381 pub handle_fields: Vec<FieldSlot>,
382 pub collection_fields: Vec<FieldSlot>,
383 pub nested_handles: Vec<NestedHandle>,
384 pub ancestors: Vec<ClassTemplateId>,
387 pub rand_props: Vec<RandProp>,
388}
389
390pub fn template_snapshot(ids: &[ClassTemplateId]) -> Vec<TemplateSnapshot> {
395 let mut ids = ids.to_vec();
396 ids.sort_by_key(|i| i.0);
397 ids.dedup();
398 let r = registry().lock().unwrap_or_else(|e| e.into_inner());
399 ids.into_iter()
400 .filter_map(|id| {
401 let info = r.get(&id)?;
402 let mut ancestors: Vec<ClassTemplateId> = info.ancestors.iter().copied().collect();
403 ancestors.sort_by_key(|i| i.0);
404 Some(TemplateSnapshot {
405 id,
406 handle_fields: info.handle_fields.clone(),
407 collection_fields: info.collection_fields.clone(),
408 nested_handles: info.nested_handles.clone(),
409 ancestors,
410 rand_props: info.rand_props.clone(),
411 })
412 })
413 .collect()
414}
415
416pub fn preload_templates(snapshot: &[TemplateSnapshot]) {
419 let mut r = registry().lock().unwrap_or_else(|e| e.into_inner());
420 for t in snapshot {
421 let info = r.entry(t.id).or_default();
422 info.handle_fields = t.handle_fields.clone();
423 info.collection_fields = t.collection_fields.clone();
424 info.nested_handles = t.nested_handles.clone();
425 info.ancestors = t.ancestors.iter().copied().collect();
426 info.rand_props = t.rand_props.clone();
427 }
428}
429
430pub fn rand_props(template: ClassTemplateId) -> Vec<RandProp> {
433 registry()
434 .lock()
435 .unwrap_or_else(|e| e.into_inner())
436 .get(&template)
437 .map(|info| info.rand_props.clone())
438 .unwrap_or_default()
439}
440
441fn index(h: u64) -> usize {
442 (h & 0xffff_ffff) as usize
443}
444
445fn version_of(h: u64) -> u32 {
446 (h >> 32) as u32
447}
448
449impl Table {
450 fn alloc(&mut self, template: ClassTemplateId, field_words: usize) -> u64 {
451 let i = match self.free.pop() {
452 Some(i) => i as usize,
453 None => {
454 self.entries.push(None);
455 self.versions.push(0);
456 self.entries.len() - 1
457 }
458 };
459 let version = self.versions[i].wrapping_add(1);
460 self.versions[i] = version;
461 let rand_mode = vec![
463 true;
464 registry()
465 .lock()
466 .unwrap_or_else(|e| e.into_inner())
467 .get(&template)
468 .map_or(0, |info| info.rand_props.len())
469 ];
470 self.entries[i] = Some(ObjEntry {
471 version,
472 template,
473 refcount: 1,
474 fields: vec![0u64; field_words].into(),
475 prng_seed: 0,
476 rand_meta: RandMeta {
477 rand_mode,
478 leaf_off: Vec::new(),
479 leaf_on: Vec::new(),
480 },
481 randc: HashMap::new(),
482 static_rand_off: HashSet::new(),
483 constraint_off: HashSet::new(),
484 });
485 (u64::from(version) << 32) | i as u64
486 }
487
488 fn get(&self, h: u64) -> Option<&ObjEntry> {
492 let i = index(h);
493 if i == 0 {
494 return None;
495 }
496 let e = self.entries.get(i)?.as_ref()?;
497 (e.version == version_of(h)).then_some(e)
498 }
499
500 fn get_mut(&mut self, h: u64) -> Option<&mut ObjEntry> {
501 let i = index(h);
502 if i == 0 {
503 return None;
504 }
505 let v = version_of(h);
506 let e = self.entries.get_mut(i)?.as_mut()?;
507 (e.version == v).then_some(e)
508 }
509}
510
511pub fn alloc(template: ClassTemplateId, field_words: usize) -> u64 {
519 ALLOCS_SINCE_GC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
520 let h = table()
521 .lock()
522 .unwrap_or_else(|e| e.into_inner())
523 .alloc(template, field_words);
524 if let Some(hook) = CREATE_HOOK.get() {
525 hook(h);
526 }
527 h
528}
529
530static CREATE_HOOK: std::sync::OnceLock<fn(u64)> = std::sync::OnceLock::new();
537static RECLAIM_HOOK: std::sync::OnceLock<fn(u64)> = std::sync::OnceLock::new();
538
539pub fn set_lifecycle_hooks(create: fn(u64), reclaim: fn(u64)) {
542 let _ = CREATE_HOOK.set(create);
543 let _ = RECLAIM_HOOK.set(reclaim);
544}
545
546pub fn retain(h: u64) {
551 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
552 if let Some(e) = t.get_mut(h) {
553 e.refcount = e.refcount.saturating_add(1);
556 }
557}
558
559pub fn release(h: u64) {
567 let mut pending = vec![h];
571 let mut freed = false;
572 let mut colls: Vec<u64> = Vec::new();
577 while let Some(h) = pending.pop() {
578 {
579 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
580 let Some(e) = t.get_mut(h) else { continue };
581 e.refcount = e.refcount.saturating_sub(1);
582 if e.refcount > 0 {
583 continue;
584 }
585 }
586 if let Some(hook) = RECLAIM_HOOK.get() {
588 hook(h);
589 }
590 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
591 let Some(e) = t.get_mut(h) else { continue };
592 if e.refcount > 0 {
594 continue;
595 }
596 let template = e.template;
597 {
598 let r = registry().lock().unwrap_or_else(|e| e.into_inner());
599 if let Some(info) = r.get(&template) {
600 for slot in &info.handle_fields {
601 if let Some(&w) = e.fields.get(slot.offset) {
602 pending.push(w);
603 }
604 }
605 for slot in &info.collection_fields {
606 if let Some(&w) = e.fields.get(slot.offset) {
607 colls.push(w);
608 }
609 }
610 for nh in &info.nested_handles {
611 if let Some(w) = nested_get(&e.fields, nh) {
612 if nh.coll {
613 colls.push(w);
614 } else {
615 pending.push(w);
616 }
617 }
618 }
619 }
620 }
621 let i = index(h);
622 t.entries[i] = None;
623 t.free.push(i as u32);
624 freed = true;
625 }
626 for c in colls {
627 crate::collections::release(c);
628 }
629 if freed {
633 crate::sched::notify_object_state_change();
634 }
635}
636
637pub fn copy(h: u64) -> Option<u64> {
646 let (template, fields, rand_meta, constraint_off, prng_seed, colls, nested_hs) = {
649 let t = table().lock().unwrap_or_else(|e| e.into_inner());
650 let src = t.get(h)?;
651 let (colls, nested_hs): (Vec<usize>, Vec<NestedHandle>) = registry()
652 .lock()
653 .unwrap_or_else(|e| e.into_inner())
654 .get(&src.template)
655 .map(|info| {
656 (
657 info.collection_fields.iter().map(|s| s.offset).collect(),
658 info.nested_handles.clone(),
659 )
660 })
661 .unwrap_or_default();
662 (
663 src.template,
664 src.fields.clone(),
665 src.rand_meta.clone(),
666 src.constraint_off.clone(),
667 src.prng_seed,
668 colls,
669 nested_hs,
670 )
671 };
672 let mut fields = fields;
673 for off in colls {
674 if let Some(w) = fields.get_mut(off) {
675 *w = crate::collections::clone(*w);
676 }
677 }
678 let mut nested_objs: Vec<u64> = Vec::new();
681 for nh in &nested_hs {
682 let Some(w) = nested_get(&fields, nh) else {
683 continue;
684 };
685 if nh.coll {
686 nested_set(&mut fields, nh, crate::collections::clone(w));
687 } else {
688 nested_objs.push(w);
689 }
690 }
691 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
692 let mut nested: Vec<u64> = registry()
693 .lock()
694 .unwrap_or_else(|e| e.into_inner())
695 .get(&template)
696 .map(|info| {
697 info.handle_fields
698 .iter()
699 .filter_map(|s| fields.get(s.offset).copied())
700 .collect()
701 })
702 .unwrap_or_default();
703 nested.extend(nested_objs);
704 let new = t.alloc(template, fields.len());
705 for n in nested {
706 if let Some(e) = t.get_mut(n) {
707 e.refcount = e.refcount.saturating_add(1);
708 }
709 }
710 if let Some(e) = t.get_mut(new) {
711 e.fields = fields;
712 e.rand_meta = rand_meta;
713 e.constraint_off = constraint_off;
714 e.prng_seed = prng_seed;
715 }
716 drop(t);
717 if let Some(hook) = CREATE_HOOK.get() {
719 hook(new);
720 }
721 Some(new)
722}
723
724pub fn live_count() -> usize {
730 table()
731 .lock()
732 .unwrap_or_else(|e| e.into_inner())
733 .entries
734 .iter()
735 .filter(|e| e.is_some())
736 .count()
737}
738
739pub fn refcount(h: u64) -> Option<u32> {
742 table()
743 .lock()
744 .unwrap_or_else(|e| e.into_inner())
745 .get(h)
746 .map(|e| e.refcount)
747}
748
749pub fn field_get(h: u64, slot: FieldSlot, out: &mut [u64]) -> bool {
753 let t = table().lock().unwrap_or_else(|e| e.into_inner());
754 let Some(e) = t.get(h) else { return false };
755 if slot.offset + slot.words > e.fields.len() {
756 return false;
757 }
758 let n = out.len().min(slot.words);
759 out[..n].copy_from_slice(&e.fields[slot.offset..slot.offset + n]);
760 true
761}
762
763pub fn field_set(h: u64, slot: FieldSlot, val: &[u64]) -> bool {
768 let changed = {
769 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
770 let Some(e) = t.get_mut(h) else { return false };
771 if slot.offset + slot.words > e.fields.len() {
772 return false;
773 }
774 let n = val.len().min(slot.words);
775 let dest = &mut e.fields[slot.offset..slot.offset + n];
776 let changed = dest != &val[..n];
777 dest.copy_from_slice(&val[..n]);
778 changed
779 };
780 if changed {
783 crate::sched::notify_object_state_change();
784 }
785 true
786}
787
788pub fn field_merge(h: u64, offset: usize, val: &[u64], mask: &[u64]) -> bool {
793 let changed = {
794 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
795 let Some(e) = t.get_mut(h) else { return false };
796 let n = val.len().min(mask.len());
797 if offset + n > e.fields.len() {
798 return false;
799 }
800 let mut changed = false;
801 for i in 0..n {
802 let old = e.fields[offset + i];
803 let new = (old & !mask[i]) | (val[i] & mask[i]);
804 changed |= new != old;
805 e.fields[offset + i] = new;
806 }
807 changed
808 };
809 if changed {
810 crate::sched::notify_object_state_change();
811 }
812 true
813}
814
815pub fn field_get_window(
819 h: u64,
820 slot: FieldSlot,
821 win: Window,
822 val: &mut [u64],
823 unk: &mut [u64],
824) -> bool {
825 let t = table().lock().unwrap_or_else(|e| e.into_inner());
826 let Some(e) = t.get(h) else { return false };
827 if slot.offset + slot.words > e.fields.len() {
828 return false;
829 }
830 win.read(&e.fields[slot.offset..slot.offset + slot.words], val, unk);
831 true
832}
833
834pub fn field_set_window(h: u64, slot: FieldSlot, win: Window, val: &[u64]) -> bool {
839 let changed = {
840 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
841 let Some(e) = t.get_mut(h) else { return false };
842 if slot.offset + slot.words > e.fields.len() {
843 return false;
844 }
845 let dest = &mut e.fields[slot.offset..slot.offset + slot.words];
846 let before = dest.to_vec();
847 win.write(dest, val);
848 before != dest
849 };
850 if changed {
851 crate::sched::notify_object_state_change();
852 }
853 true
854}
855
856pub fn is_live(h: u64) -> bool {
862 table()
863 .lock()
864 .unwrap_or_else(|e| e.into_inner())
865 .get(h)
866 .is_some()
867}
868
869pub fn slot_index(h: u64) -> u64 {
875 index(h) as u64
876}
877
878pub fn dynamic_template(h: u64) -> Option<ClassTemplateId> {
880 table()
881 .lock()
882 .unwrap_or_else(|e| e.into_inner())
883 .get(h)
884 .map(|e| e.template)
885}
886
887pub fn is_a(h: u64, dest: ClassTemplateId) -> bool {
896 let Some(template) = dynamic_template(h) else {
897 return false;
898 };
899 template_is_a(template, dest)
900}
901
902pub fn template_is_a(template: ClassTemplateId, dest: ClassTemplateId) -> bool {
907 registry()
908 .lock()
909 .unwrap_or_else(|e| e.into_inner())
910 .get(&template)
911 .is_some_and(|info| info.ancestors.contains(&dest))
912}
913
914pub fn prng_seed(h: u64) -> Option<u64> {
919 table()
920 .lock()
921 .unwrap_or_else(|e| e.into_inner())
922 .get(h)
923 .map(|e| e.prng_seed)
924}
925
926pub fn set_prng_seed(h: u64, seed: u64) {
928 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
929 if let Some(e) = t.get_mut(h) {
930 e.prng_seed = seed;
931 }
932}
933
934pub fn rand_meta(h: u64) -> Option<RandMeta> {
936 table()
937 .lock()
938 .unwrap_or_else(|e| e.into_inner())
939 .get(h)
940 .map(|e| e.rand_meta.clone())
941}
942
943pub fn set_rand_meta(h: u64, meta: RandMeta) {
945 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
946 if let Some(e) = t.get_mut(h) {
947 e.rand_meta = meta;
948 }
949}
950
951static ALLOCS_SINCE_GC: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
954
955pub fn collect_cycles() -> usize {
969 let freed = collect_cycles_locked();
970 COLLECTED.fetch_add(freed, std::sync::atomic::Ordering::Relaxed);
971 freed
972}
973
974static COLLECTED: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
976
977pub fn collected_count() -> usize {
979 COLLECTED.load(std::sync::atomic::Ordering::Relaxed)
980}
981
982fn collect_cycles_locked() -> usize {
983 ALLOCS_SINCE_GC.store(0, std::sync::atomic::Ordering::Relaxed);
984 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
985 let r = registry().lock().unwrap_or_else(|e| e.into_inner());
986 let handle_fields = |e: &ObjEntry| -> Vec<u64> {
987 r.get(&e.template)
988 .map(|info| {
989 info.handle_fields
990 .iter()
991 .filter_map(|s| e.fields.get(s.offset).copied())
992 .collect()
993 })
994 .unwrap_or_default()
995 };
996 let live: Vec<u64> = t
997 .entries
998 .iter()
999 .enumerate()
1000 .filter_map(|(i, e)| e.as_ref().map(|e| (u64::from(e.version) << 32) | i as u64))
1001 .collect();
1002 let mut internal: HashMap<usize, u32> = HashMap::new();
1003 for &h in &live {
1004 let Some(e) = t.get(h) else { continue };
1005 for w in handle_fields(e) {
1006 if t.get(w).is_some() {
1007 *internal.entry(index(w)).or_default() += 1;
1008 }
1009 }
1010 }
1011 let mut marked: HashSet<usize> = HashSet::new();
1012 let mut stack: Vec<u64> = live
1013 .iter()
1014 .copied()
1015 .filter(|&h| {
1016 t.get(h)
1017 .is_some_and(|e| e.refcount > internal.get(&index(h)).copied().unwrap_or(0))
1018 })
1019 .collect();
1020 while let Some(h) = stack.pop() {
1021 if !marked.insert(index(h)) {
1022 continue;
1023 }
1024 if let Some(e) = t.get(h) {
1025 stack.extend(handle_fields(e).into_iter().filter(|&w| t.get(w).is_some()));
1026 }
1027 }
1028 let garbage: Vec<u64> = live
1029 .into_iter()
1030 .filter(|&h| !marked.contains(&index(h)))
1031 .collect();
1032 let mut drops: Vec<u64> = Vec::new();
1033 for &g in &garbage {
1034 if let Some(e) = t.get(g) {
1035 drops.extend(
1036 handle_fields(e)
1037 .into_iter()
1038 .filter(|w| marked.contains(&index(*w))),
1039 );
1040 }
1041 }
1042 for w in drops {
1043 if let Some(e) = t.get_mut(w) {
1044 e.refcount = e.refcount.saturating_sub(1);
1045 }
1046 }
1047 for &g in &garbage {
1048 let i = index(g);
1049 t.entries[i] = None;
1050 t.free.push(i as u32);
1051 }
1052 garbage.len()
1053}
1054
1055pub fn maybe_collect_cycles() -> usize {
1059 let since = ALLOCS_SINCE_GC.load(std::sync::atomic::Ordering::Relaxed);
1060 if since < 4096 || since < LIVE_AFTER_GC.load(std::sync::atomic::Ordering::Relaxed) {
1063 return 0;
1064 }
1065 let freed = collect_cycles();
1066 LIVE_AFTER_GC.store(live_count(), std::sync::atomic::Ordering::Relaxed);
1067 freed
1068}
1069
1070static LIVE_AFTER_GC: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1073
1074pub fn rand_mode_of(h: u64, offset: usize) -> Option<bool> {
1078 let t = table().lock().unwrap_or_else(|e| e.into_inner());
1079 let e = t.get(h)?;
1080 let i = rand_prop_index(e.template, offset)?;
1081 e.rand_meta.rand_mode.get(i).copied()
1082}
1083
1084pub fn set_rand_mode_of(h: u64, offset: usize, on: bool) -> bool {
1088 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1089 let Some(e) = t.get_mut(h) else { return false };
1090 let Some(i) = rand_prop_index(e.template, offset) else {
1091 return false;
1092 };
1093 match e.rand_meta.rand_mode.get_mut(i) {
1094 Some(bit) => {
1095 *bit = on;
1096 e.rand_meta.leaf_off.retain(|&(p, _)| p != i);
1098 e.rand_meta.leaf_on.retain(|&(p, _)| p != i);
1099 true
1100 }
1101 None => false,
1102 }
1103}
1104
1105pub fn leaf_rand_mode_of(h: u64, offset: usize, bit: u32) -> Option<bool> {
1110 let t = table().lock().unwrap_or_else(|e| e.into_inner());
1111 let e = t.get(h)?;
1112 let i = rand_prop_index(e.template, offset)?;
1113 let on = *e.rand_meta.rand_mode.get(i)?;
1114 let m = &e.rand_meta;
1115 Some(if on {
1116 !m.leaf_off.contains(&(i, bit))
1117 } else {
1118 m.leaf_on.contains(&(i, bit))
1119 })
1120}
1121
1122pub fn set_leaf_rand_mode_of(h: u64, offset: usize, bit: u32, on: bool) -> bool {
1125 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1126 let Some(e) = t.get_mut(h) else { return false };
1127 let Some(i) = rand_prop_index(e.template, offset) else {
1128 return false;
1129 };
1130 let m = &mut e.rand_meta;
1131 if i >= m.rand_mode.len() {
1132 return false;
1133 }
1134 m.leaf_off.retain(|&k| k != (i, bit));
1135 m.leaf_on.retain(|&k| k != (i, bit));
1136 if m.rand_mode[i] && !on {
1138 m.leaf_off.push((i, bit));
1139 } else if !m.rand_mode[i] && on {
1140 m.leaf_on.push((i, bit));
1141 }
1142 true
1143}
1144
1145pub fn set_all_rand_modes(h: u64, on: bool) -> bool {
1149 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1150 let Some(e) = t.get_mut(h) else { return false };
1151 e.rand_meta.rand_mode.iter_mut().for_each(|b| *b = on);
1152 e.rand_meta.leaf_off.clear();
1153 e.rand_meta.leaf_on.clear();
1154 true
1155}
1156
1157fn rand_prop_index(template: ClassTemplateId, offset: usize) -> Option<usize> {
1160 registry()
1161 .lock()
1162 .unwrap_or_else(|e| e.into_inner())
1163 .get(&template)?
1164 .rand_props
1165 .iter()
1166 .position(|p| p.slot.offset == offset)
1167}
1168
1169pub fn static_rand_mode(h: u64, offset: usize) -> Option<bool> {
1173 table()
1174 .lock()
1175 .unwrap_or_else(|e| e.into_inner())
1176 .get(h)
1177 .map(|e| !e.static_rand_off.contains(&offset))
1178}
1179
1180pub fn set_static_rand_mode(h: u64, offset: usize, on: bool) -> bool {
1183 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1184 let Some(e) = t.get_mut(h) else { return false };
1185 if on {
1186 e.static_rand_off.remove(&offset);
1187 } else {
1188 e.static_rand_off.insert(offset);
1189 }
1190 true
1191}
1192
1193pub fn constraint_mode_of(h: u64, ordinal: u32) -> Option<bool> {
1197 table()
1198 .lock()
1199 .unwrap_or_else(|e| e.into_inner())
1200 .get(h)
1201 .map(|e| !e.constraint_off.contains(&ordinal))
1202}
1203
1204fn static_singleton_keys() -> &'static Mutex<HashMap<String, ClassTemplateId>> {
1208 static K: OnceLock<Mutex<HashMap<String, ClassTemplateId>>> = OnceLock::new();
1209 K.get_or_init(|| Mutex::new(HashMap::new()))
1210}
1211
1212pub fn register_static_singleton_key(key: &str, template: ClassTemplateId) {
1215 static_singleton_keys()
1216 .lock()
1217 .unwrap_or_else(|e| e.into_inner())
1218 .insert(key.to_owned(), template);
1219}
1220
1221pub fn static_singleton_key_pairs() -> Vec<(String, u32)> {
1225 let mut v: Vec<(String, u32)> = static_singleton_keys()
1226 .lock()
1227 .unwrap_or_else(|e| e.into_inner())
1228 .iter()
1229 .map(|(k, t)| (k.clone(), t.0))
1230 .collect();
1231 v.sort();
1232 v
1233}
1234
1235pub fn static_singleton_of(key: &str) -> Option<Option<u64>> {
1240 let template = *static_singleton_keys()
1241 .lock()
1242 .unwrap_or_else(|e| e.into_inner())
1243 .get(key)?;
1244 let t = table().lock().unwrap_or_else(|e| e.into_inner());
1245 Some(t.entries.iter().enumerate().find_map(|(i, e)| {
1246 let e = e.as_ref()?;
1247 (e.template == template).then(|| (u64::from(e.version) << 32) | i as u64)
1248 }))
1249}
1250
1251pub fn set_constraint_mode_of(h: u64, ordinal: u32, on: bool) -> bool {
1254 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1255 let Some(e) = t.get_mut(h) else { return false };
1256 if on {
1257 e.constraint_off.remove(&ordinal);
1258 } else {
1259 e.constraint_off.insert(ordinal);
1260 }
1261 true
1262}
1263
1264pub fn randc_state(h: u64, offset: usize) -> Option<RandcState> {
1268 table()
1269 .lock()
1270 .unwrap_or_else(|e| e.into_inner())
1271 .get(h)
1272 .map(|e| e.randc.get(&offset).cloned().unwrap_or_default())
1273}
1274
1275pub fn set_randc_states(h: u64, states: Vec<(usize, RandcState)>) -> bool {
1278 let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1279 let Some(e) = t.get_mut(h) else { return false };
1280 e.randc.extend(states);
1281 true
1282}
1283
1284#[cfg(test)]
1285mod tests {
1286 use super::*;
1287
1288 #[test]
1301 fn is_live_tracks_release_and_rejects_a_stale_handle_after_slot_reuse() {
1302 assert!(!is_live(0), "the null handle is never live");
1303 let a = alloc(ClassTemplateId(1100), 0);
1304 assert!(is_live(a));
1305 release(a);
1306 assert!(!is_live(a), "a freed handle is not live");
1307 let b = alloc(ClassTemplateId(1101), 0);
1310 assert!(is_live(b));
1311 assert!(!is_live(a), "a stale handle must not alias the newcomer");
1312 release(b);
1313 }
1314
1315 #[test]
1316 fn slot_index_is_nonzero_distinct_and_zero_for_null() {
1317 assert_eq!(slot_index(0), 0);
1318 let a = alloc(ClassTemplateId(1102), 0);
1319 let b = alloc(ClassTemplateId(1103), 0);
1320 assert_ne!(slot_index(a), 0);
1321 assert_ne!(slot_index(a), slot_index(b));
1322 release(a);
1323 release(b);
1324 }
1325
1326 #[test]
1327 fn alloc_field_set_field_get_round_trips() {
1328 let h = alloc(ClassTemplateId(1000), 2);
1329 let slot0 = FieldSlot {
1330 offset: 0,
1331 words: 1,
1332 };
1333 let slot1 = FieldSlot {
1334 offset: 1,
1335 words: 1,
1336 };
1337 field_set(h, slot0, &[42]);
1338 field_set(h, slot1, &[7]);
1339 let mut out = [0u64];
1340 assert!(field_get(h, slot0, &mut out));
1341 assert_eq!(out, [42]);
1342 assert!(field_get(h, slot1, &mut out));
1343 assert_eq!(out, [7]);
1344 assert_eq!(dynamic_template(h), Some(ClassTemplateId(1000)));
1345 }
1346
1347 #[test]
1348 fn two_retains_need_two_releases_before_the_slot_frees() {
1349 let h = alloc(ClassTemplateId(1001), 1);
1353 let slot = FieldSlot {
1354 offset: 0,
1355 words: 1,
1356 };
1357 field_set(h, slot, &[0xdead_beef]);
1358 retain(h); release(h); let mut out = [0u64];
1361 assert!(
1362 field_get(h, slot, &mut out),
1363 "one release of two retains must not free the slot"
1364 );
1365 assert_eq!(
1366 out,
1367 [0xdead_beef],
1368 "a leaked (still-referenced) slot's data is intact"
1369 );
1370 release(h); assert!(
1372 !field_get(h, slot, &mut out),
1373 "the second release must free the slot"
1374 );
1375 }
1376
1377 #[test]
1378 fn releasing_an_object_recursively_releases_a_handle_field_it_points() {
1379 let pointee_template = ClassTemplateId(1002);
1380 let pointer_template = ClassTemplateId(1003);
1381 let pointee = alloc(pointee_template, 1);
1382 let pointer = alloc(pointer_template, 1);
1383 let handle_slot = FieldSlot {
1384 offset: 0,
1385 words: 1,
1386 };
1387 field_set(pointer, handle_slot, &[pointee]);
1388 register_handle_fields(pointer_template, vec![handle_slot]);
1389
1390 retain(pointee); assert_eq!(dynamic_template(pointee), Some(pointee_template));
1392
1393 release(pointer); let mut out = [0u64];
1395 assert!(
1396 field_get(pointee, handle_slot, &mut out),
1397 "pointee still has one more reference after the recursive release"
1398 );
1399
1400 release(pointee);
1402 assert!(!field_get(pointee, handle_slot, &mut out));
1403 assert_eq!(dynamic_template(pointee), None);
1404 }
1405
1406 #[test]
1413 fn a_stale_handle_after_its_slot_is_recycled_fails_field_get() {
1414 let mut t = Table {
1419 entries: vec![None],
1420 versions: vec![0],
1421 free: Vec::new(),
1422 };
1423 let h1 = t.alloc(ClassTemplateId(0), 1);
1424 t.get_mut(h1).unwrap().fields[0] = 1;
1425
1426 let i = index(h1);
1429 t.entries[i] = None;
1430 t.free.push(i as u32);
1431
1432 let h2 = t.alloc(ClassTemplateId(1), 1);
1433 assert_eq!(
1434 index(h1),
1435 index(h2),
1436 "the only freed slot in a fresh table is always the next one `alloc` reuses"
1437 );
1438 assert_ne!(
1439 h1, h2,
1440 "alloc must bump the recycled index's version, or the stale handle would still match"
1441 );
1442 assert!(
1443 t.get(h1).is_none(),
1444 "a stale (old_version, index) handle must fail to resolve once its slot is recycled"
1445 );
1446 let e2 = t
1447 .get(h2)
1448 .expect("the new object at the recycled index is live");
1449 assert_eq!(
1450 e2.fields[0], 0,
1451 "the new object's fields start zeroed -- never carrying over the old occupant's data"
1452 );
1453 }
1454
1455 #[test]
1456 fn is_a_matches_self_and_registered_ancestors_only() {
1457 let base = ClassTemplateId(100);
1458 let derived = ClassTemplateId(101);
1459 let unrelated = ClassTemplateId(102);
1460 register_ancestors(derived, HashSet::from([base]));
1461 register_ancestors(base, HashSet::new());
1462
1463 let h = alloc(derived, 0);
1464 assert!(
1465 is_a(h, derived),
1466 "an object is always is_a its own dynamic class"
1467 );
1468 assert!(is_a(h, base), "a derived object is_a its registered base");
1469 assert!(
1470 !is_a(h, unrelated),
1471 "is_a must not match a template that was never registered as an ancestor"
1472 );
1473
1474 let base_h = alloc(base, 0);
1475 assert!(
1476 !is_a(base_h, derived),
1477 "is_a is one-directional: a base object is never is_a its derived class"
1478 );
1479 }
1480
1481 #[test]
1482 fn retain_release_and_field_access_on_the_null_handle_are_safe_no_ops() {
1483 assert_eq!(dynamic_template(0), None);
1484 assert!(!is_a(0, ClassTemplateId(1004)));
1485 retain(0);
1486 release(0);
1487 let slot = FieldSlot {
1488 offset: 0,
1489 words: 1,
1490 };
1491 let mut out = [0u64];
1492 assert!(!field_get(0, slot, &mut out));
1493 field_set(0, slot, &[1]); }
1495
1496 #[test]
1497 fn prng_seed_and_rand_meta_round_trip_and_are_none_once_freed() {
1498 let h = alloc(ClassTemplateId(1005), 0);
1499 assert_eq!(
1500 prng_seed(h),
1501 Some(0),
1502 "freshly allocated, before any seeding hook runs"
1503 );
1504 set_prng_seed(h, 0x1234_5678);
1505 assert_eq!(prng_seed(h), Some(0x1234_5678));
1506
1507 assert_eq!(
1508 rand_meta(h),
1509 Some(RandMeta::default()),
1510 "a template with no registered rand properties has no rand_mode bits"
1511 );
1512 set_rand_meta(
1513 h,
1514 RandMeta {
1515 rand_mode: vec![true, false],
1516 ..Default::default()
1517 },
1518 );
1519 assert_eq!(
1520 rand_meta(h),
1521 Some(RandMeta {
1522 rand_mode: vec![true, false],
1523 ..Default::default()
1524 })
1525 );
1526
1527 release(h);
1528 assert_eq!(prng_seed(h), None);
1529 assert_eq!(rand_meta(h), None);
1530 }
1531
1532 #[test]
1533 fn rand_props_are_template_level_and_every_object_starts_all_active() {
1534 let t = ClassTemplateId(1040);
1535 let a = RandProp {
1536 slot: FieldSlot {
1537 offset: 0,
1538 words: 1,
1539 },
1540 kind: RandKind::Rand,
1541 };
1542 let b = RandProp {
1543 slot: FieldSlot {
1544 offset: 2,
1545 words: 2,
1546 },
1547 kind: RandKind::RandC,
1548 };
1549 assert_eq!(rand_props(t), Vec::new(), "nothing registered yet");
1550 register_rand_props(t, vec![a, b]);
1551 assert_eq!(rand_props(t), vec![a, b]);
1552 let h1 = alloc(t, 4);
1553 let h2 = alloc(t, 4);
1554 let all_on = RandMeta {
1555 rand_mode: vec![true, true],
1556 ..Default::default()
1557 };
1558 assert_eq!(rand_meta(h1), Some(all_on.clone()));
1559 set_rand_meta(
1562 h1,
1563 RandMeta {
1564 rand_mode: vec![true, false],
1565 ..Default::default()
1566 },
1567 );
1568 assert_eq!(rand_meta(h2), Some(all_on));
1569 let dup = copy(h1).unwrap();
1570 assert_eq!(rand_meta(dup).unwrap().rand_mode, vec![true, false]);
1571 register_rand_props(t, vec![a]);
1573 assert_eq!(rand_props(t), vec![a]);
1574 let h3 = alloc(t, 4);
1575 assert_eq!(rand_meta(h3).unwrap().rand_mode, vec![true]);
1576 for h in [h1, h2, h3, dup] {
1577 release(h);
1578 }
1579 }
1580
1581 fn one_word(offset: usize) -> FieldSlot {
1582 FieldSlot { offset, words: 1 }
1583 }
1584
1585 #[test]
1586 fn refcount_tracks_retain_and_release_and_is_none_when_dead() {
1587 let h = alloc(ClassTemplateId(1006), 1);
1588 assert_eq!(refcount(h), Some(1));
1589 retain(h);
1590 assert_eq!(refcount(h), Some(2));
1591 release(h);
1592 assert_eq!(refcount(h), Some(1));
1593 release(h);
1594 assert_eq!(refcount(h), None, "a freed handle has no count");
1595 assert_eq!(refcount(0), None, "null has no count");
1596 }
1597
1598 #[test]
1599 fn live_count_sees_an_allocation_until_its_last_release() {
1600 let h = alloc(ClassTemplateId(1007), 1);
1604 assert!(live_count() >= 1);
1605 release(h);
1606 assert_eq!(refcount(h), None);
1607 }
1608
1609 #[test]
1610 fn copy_is_shallow_of_the_dynamic_class_and_retains_nested_handles() {
1611 let inner_t = ClassTemplateId(1008);
1614 let outer_t = ClassTemplateId(1009);
1615 register_handle_fields(outer_t, vec![one_word(1)]);
1616 let inner = alloc(inner_t, 1);
1617 field_set(inner, one_word(0), &[5]);
1618 let src = alloc(outer_t, 2);
1619 field_set(src, one_word(0), &[77]);
1620 field_set(src, one_word(1), &[inner]); assert_eq!(refcount(inner), Some(1));
1622
1623 let dup = copy(src).expect("a live source copies");
1624 assert_ne!(dup, src, "a copy is a distinct object");
1625 assert_eq!(dynamic_template(dup), Some(outer_t));
1626 assert_eq!(refcount(dup), Some(1), "the caller owns the copy");
1627 assert_eq!(refcount(src), Some(1), "the source is untouched");
1628 assert_eq!(
1629 refcount(inner),
1630 Some(2),
1631 "the nested object is shared: both owners hold a reference"
1632 );
1633 let mut out = [0u64];
1634 assert!(field_get(dup, one_word(0), &mut out));
1635 assert_eq!(out, [77], "scalar properties are copied by value");
1636 assert!(field_get(dup, one_word(1), &mut out));
1637 assert_eq!(out, [inner], "the handle is copied, not the object");
1638
1639 field_set(dup, one_word(0), &[88]);
1641 assert!(field_get(src, one_word(0), &mut out));
1642 assert_eq!(out, [77]);
1643
1644 release(src);
1647 assert_eq!(refcount(inner), Some(1));
1648 release(dup);
1649 assert_eq!(refcount(inner), None);
1650 }
1651
1652 #[test]
1653 fn copy_keeps_the_prng_seed_and_rand_meta() {
1654 let src = alloc(ClassTemplateId(1010), 0);
1655 set_prng_seed(src, 99);
1656 set_rand_meta(
1657 src,
1658 RandMeta {
1659 rand_mode: vec![false, true],
1660 ..Default::default()
1661 },
1662 );
1663 let dup = copy(src).unwrap();
1664 assert_eq!(prng_seed(dup), Some(99));
1665 assert_eq!(
1666 rand_meta(dup),
1667 Some(RandMeta {
1668 rand_mode: vec![false, true],
1669 ..Default::default()
1670 })
1671 );
1672 release(src);
1673 release(dup);
1674 }
1675
1676 #[test]
1679 fn leaf_rand_mode_is_per_element_and_reset_by_the_whole_property() {
1680 let t = ClassTemplateId(1031);
1681 register_rand_props(
1682 t,
1683 vec![RandProp {
1684 slot: one_word(0),
1685 kind: RandKind::Rand,
1686 }],
1687 );
1688 let h = alloc(t, 2);
1689 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(true));
1690 assert!(set_leaf_rand_mode_of(h, 0, 8, false));
1691 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(false));
1692 assert_eq!(leaf_rand_mode_of(h, 0, 0), Some(true));
1693 assert_eq!(rand_mode_of(h, 0), Some(true));
1694 assert!(
1695 !set_leaf_rand_mode_of(h, 1, 8, false),
1696 "offset 1 is no rand property"
1697 );
1698 assert!(set_rand_mode_of(h, 0, true));
1699 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(true));
1700 assert!(set_leaf_rand_mode_of(h, 0, 8, false));
1701 assert!(set_all_rand_modes(h, true));
1702 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(true));
1703 assert!(set_rand_mode_of(h, 0, false));
1705 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(false));
1706 assert!(set_leaf_rand_mode_of(h, 0, 8, true));
1707 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(true));
1708 assert_eq!(leaf_rand_mode_of(h, 0, 0), Some(false));
1709 assert!(set_leaf_rand_mode_of(h, 0, 8, false));
1710 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(false));
1711 assert!(set_leaf_rand_mode_of(h, 0, 8, true));
1712 assert!(set_rand_mode_of(h, 0, false));
1713 assert_eq!(leaf_rand_mode_of(h, 0, 8), Some(false), "whole call resets");
1714 release(h);
1715 }
1716
1717 #[test]
1721 fn rand_mode_is_addressed_by_property_offset() {
1722 let t = ClassTemplateId(1030);
1723 register_rand_props(
1724 t,
1725 vec![
1726 RandProp {
1727 slot: one_word(0),
1728 kind: RandKind::Rand,
1729 },
1730 RandProp {
1731 slot: one_word(3),
1732 kind: RandKind::RandC,
1733 },
1734 ],
1735 );
1736 let h = alloc(t, 4);
1737 assert_eq!(rand_mode_of(h, 0), Some(true));
1738 assert_eq!(rand_mode_of(h, 3), Some(true));
1739 assert!(set_rand_mode_of(h, 3, false));
1740 assert_eq!(rand_mode_of(h, 0), Some(true));
1741 assert_eq!(rand_mode_of(h, 3), Some(false));
1742 assert_eq!(rand_mode_of(h, 1), None);
1744 assert!(!set_rand_mode_of(h, 1, false));
1745 assert_eq!(rand_meta(h).unwrap().rand_mode, vec![true, false]);
1746 assert!(set_all_rand_modes(h, false));
1747 assert_eq!(rand_meta(h).unwrap().rand_mode, vec![false, false]);
1748 assert!(set_all_rand_modes(h, true));
1749 assert_eq!(rand_meta(h).unwrap().rand_mode, vec![true, true]);
1750 release(h);
1751 assert_eq!(rand_mode_of(h, 0), None);
1753 assert!(!set_rand_mode_of(h, 0, true));
1754 assert!(!set_all_rand_modes(h, true));
1755 }
1756
1757 #[test]
1761 fn static_rand_and_constraint_modes_are_kept_per_holding_object() {
1762 let t = ClassTemplateId(1031);
1763 let h = alloc(t, 2);
1764 assert_eq!(static_rand_mode(h, 1), Some(true));
1765 assert_eq!(constraint_mode_of(h, 2), Some(true));
1766 assert!(set_static_rand_mode(h, 1, false));
1767 assert!(set_constraint_mode_of(h, 2, false));
1768 assert_eq!(static_rand_mode(h, 1), Some(false));
1769 assert_eq!(static_rand_mode(h, 0), Some(true));
1770 assert_eq!(constraint_mode_of(h, 2), Some(false));
1771 assert_eq!(constraint_mode_of(h, 1), Some(true));
1772 let dup = copy(h).unwrap();
1773 assert_eq!(constraint_mode_of(dup, 2), Some(false));
1774 assert!(set_constraint_mode_of(h, 2, true));
1775 assert_eq!(constraint_mode_of(h, 2), Some(true));
1776 assert_eq!(constraint_mode_of(dup, 2), Some(false));
1777 assert!(set_static_rand_mode(h, 1, true));
1778 assert_eq!(static_rand_mode(h, 1), Some(true));
1779 release(h);
1780 release(dup);
1781 assert_eq!(static_rand_mode(h, 1), None);
1782 assert_eq!(constraint_mode_of(h, 2), None);
1783 assert!(!set_static_rand_mode(h, 1, false));
1784 assert!(!set_constraint_mode_of(h, 2, false));
1785 }
1786
1787 #[test]
1790 fn a_collection_field_is_freed_with_its_object_and_cloned_by_copy() {
1791 use crate::collections::{self, KeyKind, Shape};
1792 let shape = Shape {
1793 elem_words: 1,
1794 assoc: false,
1795 key: KeyKind::Int {
1796 width: 32,
1797 signed: true,
1798 },
1799 obj: false,
1800 };
1801 let t = ClassTemplateId(1060);
1802 register_collection_fields(t, vec![one_word(1)]);
1803 let src = alloc(t, 2);
1804 let q = collections::set(&shape, 0, 0, &[7], true);
1805 let q = collections::set(&shape, q, 1, &[8], true);
1806 assert_eq!(collections::size(q), 2);
1807 field_set(src, one_word(1), &[q]);
1808
1809 let dup = copy(src).expect("copies");
1810 let mut out = [0u64];
1811 assert!(field_get(dup, one_word(1), &mut out));
1812 let q2 = out[0];
1813 assert_ne!(q2, q, "the copy has its own collection handle");
1814 assert_eq!(collections::size(q2), 2);
1815 let q2b = collections::set(&shape, q2, 0, &[99], true);
1817 let mut e = [0u64];
1818 assert!(collections::get(q, 0, &mut e));
1819 assert_eq!(e, [7]);
1820 assert!(collections::get(q2b, 0, &mut e));
1821 assert_eq!(e, [99]);
1822 field_set(dup, one_word(1), &[q2b]);
1823
1824 release(src);
1825 assert!(!collections::is_live(q), "freed with the owning object");
1826 assert_eq!(collections::size(q2b), 2, "the copy's collection survives");
1827 release(dup);
1828 assert!(!collections::is_live(q2b));
1829 }
1830
1831 #[test]
1835 fn nested_handles_at_unaligned_bit_offsets_follow_the_object() {
1836 use crate::collections::{self, KeyKind, Shape};
1837 let shape = Shape {
1838 elem_words: 1,
1839 assoc: false,
1840 key: KeyKind::Int {
1841 width: 32,
1842 signed: true,
1843 },
1844 obj: false,
1845 };
1846 let t = ClassTemplateId(1021);
1849 let slot = FieldSlot {
1850 offset: 0,
1851 words: 5,
1852 };
1853 let obj_at = NestedHandle {
1854 slot,
1855 bit: 8,
1856 coll: false,
1857 };
1858 let coll_at = NestedHandle {
1859 slot,
1860 bit: 72,
1861 coll: true,
1862 };
1863 register_nested_handles(t, vec![obj_at, coll_at]);
1864 let leaf = alloc(ClassTemplateId(1022), 1);
1865 let q = collections::set(&shape, 0, 0, &[5], true);
1866 let src = alloc(t, 5);
1867 let mut fields = vec![0u64; 5];
1868 nested_set(&mut fields, &obj_at, leaf);
1870 nested_set(&mut fields, &coll_at, q);
1871 assert_eq!(nested_get(&fields, &obj_at), Some(leaf));
1872 assert_eq!(nested_get(&fields, &coll_at), Some(q), "no overlap clobber");
1873 assert!(field_set(src, slot, &fields));
1874
1875 let dup = copy(src).expect("copies");
1876 assert_eq!(
1877 refcount(leaf),
1878 Some(2),
1879 "the copy retains the nested object"
1880 );
1881 let mut out = vec![0u64; 5];
1882 assert!(field_get(dup, slot, &mut out));
1883 let q2 = nested_get(&out, &coll_at).expect("in range");
1884 assert_ne!(q2, q, "the copy owns a clone of the nested collection");
1885 assert_eq!(collections::size(q2), 1);
1886 assert_eq!(nested_get(&out, &obj_at), Some(leaf));
1887
1888 release(src);
1889 assert_eq!(
1890 refcount(leaf),
1891 Some(1),
1892 "the original dropped its reference"
1893 );
1894 assert!(
1895 !collections::is_live(q),
1896 "the nested collection died with it"
1897 );
1898 release(dup);
1899 assert_eq!(refcount(leaf), None, "the last holder freed it");
1900 assert!(!collections::is_live(q2));
1901 }
1902
1903 #[test]
1907 fn windows_read_and_write_only_their_own_bits() {
1908 let slot = FieldSlot {
1910 offset: 1,
1911 words: 4,
1912 };
1913 let h = alloc(ClassTemplateId(1031), 5);
1914 assert!(field_set(h, slot, &[u64::MAX; 4]));
1916 let wide = Window {
1918 bit: 5,
1919 width: 70,
1920 field_width: 100,
1921 };
1922 let mut val = [0u64; 2];
1923 let mut unk = [0u64; 2];
1924 assert!(field_get_window(h, slot, wide, &mut val, &mut unk));
1925 assert_eq!(val, [u64::MAX, 0x3f]);
1926 assert_eq!(unk, [u64::MAX, 0x3f]);
1927
1928 assert!(field_set_window(
1929 h,
1930 slot,
1931 wide,
1932 &[0x0123_4567_89ab_cdef, 0x15]
1933 ));
1934 assert!(field_get_window(h, slot, wide, &mut val, &mut unk));
1935 assert_eq!(val, [0x0123_4567_89ab_cdef, 0x15]);
1936 assert_eq!(unk, [0, 0], "a write makes the leaf known");
1937
1938 let low = Window {
1940 bit: 0,
1941 width: 5,
1942 field_width: 100,
1943 };
1944 let high = Window {
1945 bit: 75,
1946 width: 25,
1947 field_width: 100,
1948 };
1949 let (mut v, mut u) = ([0u64; 1], [0u64; 1]);
1950 assert!(field_get_window(h, slot, low, &mut v, &mut u));
1951 assert_eq!((v[0], u[0]), (0x1f, 0x1f));
1952 assert!(field_get_window(h, slot, high, &mut v, &mut u));
1953 assert_eq!((v[0], u[0]), ((1 << 25) - 1, (1 << 25) - 1));
1954
1955 let one = Window {
1957 bit: 64,
1958 width: 8,
1959 field_width: 100,
1960 };
1961 assert!(field_set_window(h, slot, one, &[0xa5]));
1962 assert!(field_get_window(h, slot, one, &mut v, &mut u));
1963 assert_eq!((v[0], u[0]), (0xa5, 0));
1964
1965 release(h);
1967 assert!(!field_set_window(h, slot, one, &[1]));
1968 assert!(!field_get_window(h, slot, one, &mut v, &mut u));
1969 }
1970
1971 #[test]
1972 fn copy_of_null_or_a_stale_handle_is_none() {
1973 assert_eq!(copy(0), None);
1974 let h = alloc(ClassTemplateId(1011), 1);
1975 release(h);
1976 assert_eq!(copy(h), None);
1977 }
1978
1979 #[test]
1980 fn releasing_the_head_of_a_long_chain_frees_it_without_recursion() {
1981 let t = ClassTemplateId(1012);
1984 register_handle_fields(t, vec![one_word(0)]);
1985 let head = alloc(t, 1);
1986 let mut prev = head;
1987 let mut all = vec![head];
1988 for _ in 0..100_000 {
1989 let next = alloc(t, 1);
1990 field_set(prev, one_word(0), &[next]);
1991 all.push(next);
1992 prev = next;
1993 }
1994 release(head);
1995 assert!(all.iter().all(|&h| refcount(h).is_none()));
1996 }
1997
1998 #[test]
2004 fn a_dropped_cycle_is_collected_and_what_it_shared_survives() {
2005 let t = ClassTemplateId(1900);
2006 let next = FieldSlot {
2007 offset: 0,
2008 words: 1,
2009 };
2010 let other = FieldSlot {
2011 offset: 1,
2012 words: 1,
2013 };
2014 register_handle_fields(t, vec![next, other]);
2015 let a = alloc(t, 2);
2016 let b = alloc(t, 2);
2017 let shared = alloc(t, 2);
2018 field_set(a, next, &[b]);
2020 retain(b);
2021 field_set(b, next, &[a]);
2022 retain(a);
2023 field_set(a, other, &[shared]);
2024 retain(shared);
2025 field_set(b, other, &[shared]);
2026 retain(shared);
2027 release(a);
2029 release(b);
2030 assert_eq!(refcount(a), Some(1), "reference counting alone leaks");
2031 assert_eq!(refcount(shared), Some(3));
2032 let c = alloc(t, 2);
2034 let d = alloc(t, 2);
2035 field_set(c, next, &[d]);
2036 retain(d);
2037 field_set(d, next, &[c]);
2038 retain(c);
2039 release(d);
2040 assert!(collect_cycles() >= 2);
2041 assert_eq!(refcount(a), None);
2042 assert_eq!(refcount(b), None);
2043 assert_eq!(refcount(shared), Some(1));
2044 assert_eq!(refcount(c), Some(2));
2045 assert_eq!(refcount(d), Some(1));
2046 release(c);
2047 assert!(collect_cycles() >= 2);
2048 assert_eq!(refcount(c), None);
2049 assert_eq!(refcount(d), None);
2050 release(shared);
2051 assert_eq!(refcount(shared), None);
2052 }
2053
2054 #[test]
2057 fn randc_state_is_per_object_per_offset_and_a_copy_starts_fresh() {
2058 let t = ClassTemplateId(1013);
2059 let a = alloc(t, 2);
2060 let b = alloc(t, 2);
2061 assert_eq!(randc_state(a, 0), Some(RandcState::default()));
2062 let st = RandcState {
2063 live: true,
2064 fingerprint: 7,
2065 remaining: vec![3, 1],
2066 };
2067 assert!(set_randc_states(a, vec![(1, st.clone())]));
2068 assert_eq!(randc_state(a, 1), Some(st));
2069 assert_eq!(randc_state(a, 0), Some(RandcState::default()));
2070 assert_eq!(randc_state(b, 1), Some(RandcState::default()), "per object");
2071 let c = copy(a).unwrap();
2072 assert_eq!(randc_state(c, 1), Some(RandcState::default()), "fresh copy");
2073 release(a);
2074 assert_eq!(randc_state(a, 1), None);
2075 assert!(!set_randc_states(a, vec![(1, RandcState::default())]));
2076 release(b);
2077 release(c);
2078 }
2079}