1use std::marker::PhantomData;
5
6use crate::{GcNode, GcRef, heap::GcHeap};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(transparent)]
12pub struct GcWeakRawId(u32);
13
14impl GcWeakRawId {
15 pub(crate) const NULL: Self = Self(0);
16
17 pub const fn index(&self) -> u16 {
18 (self.0 >> 16) as u16
19 }
20
21 pub const fn version(&self) -> u16 {
22 self.0 as u16
23 }
24
25 pub const fn is_null(&self) -> bool {
26 self.version() == 0
27 }
28}
29
30#[derive(PartialEq)]
32#[repr(transparent)]
33pub struct GcWeak<T: GcNode> {
34 pub(crate) weak_id: GcWeakRawId,
37
38 pub(crate) _marker: PhantomData<T>,
39}
40
41impl<T: GcNode> Clone for GcWeak<T> {
42 fn clone(&self) -> Self {
43 *self
44 }
45}
46
47impl<T: GcNode> Copy for GcWeak<T> {}
48
49impl<T: GcNode> Default for GcWeak<T> {
50 fn default() -> Self {
51 Self {
52 weak_id: GcWeakRawId::NULL,
53 _marker: PhantomData,
54 }
55 }
56}
57
58impl<T: GcNode> std::fmt::Debug for GcWeak<T> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "GcWeak({}#{})", self.index(), self.version())
61 }
62}
63
64impl<T: GcNode> GcWeak<T> {
65 pub(crate) fn new(index: u16, version: u16) -> Self {
66 debug_assert!(version > 0);
67 Self {
68 weak_id: GcWeakRawId(((index as u32) << 16) | (version as u32)),
69 _marker: PhantomData,
70 }
71 }
72
73 pub(crate) fn from_id(weak_id: GcWeakRawId) -> Self {
74 Self {
75 weak_id,
76 _marker: PhantomData,
77 }
78 }
79
80 #[inline(always)]
82 pub fn index(&self) -> u16 {
83 self.weak_id.index()
84 }
85
86 #[inline(always)]
87 pub fn version(&self) -> u16 {
88 self.weak_id.version()
89 }
90
91 #[inline(always)]
93 pub fn upgrade(&self, heap: &GcHeap) -> Option<GcRef<T>> {
94 heap.upgrade(self)
95 }
96}
97
98impl GcHeap {
99 pub fn downgrade<T: GcNode>(&mut self, gc_ref: &GcRef<T>) -> GcWeak<T> {
101 let node = unsafe {
102 let mut h = gc_ref.head_ptr;
103 h.as_mut()
104 };
105
106 if !node.weak_id.is_null() {
107 #[cfg(debug_assertions)]
109 {
110 debug_assert!((node.weak_id.index() as usize) < self.weak_slots.len());
111 let (ver, ptr) = self.weak_slots[node.weak_id.index() as usize];
112 debug_assert!(
113 ver == node.weak_id.version() && ptr.is_some_and(|p| p == gc_ref.head_ptr)
114 );
115 }
116 GcWeak::from_id(node.weak_id)
117 } else {
118 if self.weak_slots.len() == u16::MAX as usize {
119 panic!("too may weakrefs");
120 }
121
122 let i = self
124 .weak_slots
125 .iter()
126 .position(|(_, slot)| slot.is_none())
127 .unwrap_or_else(|| {
128 let n = self.weak_slots.len();
129 self.weak_slots.push((0, None));
130 n
131 });
132
133 unsafe {
134 let curr_ver = self.weak_slots.get_unchecked(i).0;
136 let version = if curr_ver == u16::MAX {
137 1
138 } else {
139 curr_ver + 1
140 };
141
142 let weak = GcWeak::new(i as _, version);
143 *self.weak_slots.get_unchecked_mut(i) = (version, Some(gc_ref.head_ptr));
144 node.weak_id = weak.weak_id;
145
146 weak
147 }
148 }
149 }
150
151 pub fn upgrade<T: GcNode>(&self, weak_ref: &GcWeak<T>) -> Option<GcRef<T>> {
153 if !weak_ref.weak_id.is_null() {
154 self.weak_slots
155 .get(weak_ref.index() as usize)
156 .and_then(|(version, node)| {
157 if *version == weak_ref.version() {
158 *node
159 } else {
160 None
161 }
162 })
163 .map(|ptr| GcRef {
164 head_ptr: ptr,
165 _marker: PhantomData,
166 })
167 } else {
168 None
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use crate::{GcTrace, GcTraceCtx, gc_type_register};
176
177 use super::*;
178
179 #[derive(Debug, Default, PartialEq, Eq)]
180 struct TestData1;
181
182 impl GcTrace for TestData1 {
183 fn trace(&self, _: &mut GcTraceCtx) {}
184 }
185
186 #[derive(Debug, Default, PartialEq, Eq)]
187 struct TestData2;
188
189 impl GcTrace for TestData2 {
190 fn trace(&self, _: &mut GcTraceCtx) {}
191 }
192
193 gc_type_register! {
194 TestData1;
195 TestData2;
196 }
197
198 #[test]
200 fn test_weak_ref_clone_and_copy() {
201 let weak1 = GcWeak::<TestData1>::new(10, 1);
202
203 let weak2 = weak1;
205 assert_eq!(weak1.index(), weak2.index());
206
207 let weak3 = weak1;
209 let weak4 = weak1; assert_eq!(weak3.index(), weak4.index());
211 }
212
213 #[test]
215 fn test_weak_ref_equality() {
216 let weak1 = GcWeak::<TestData1>::new(5, 1);
217 let weak2 = GcWeak::<TestData1>::new(5, 1);
218 let weak3 = GcWeak::<TestData1>::new(10, 1);
219
220 assert_eq!(weak1, weak2);
222
223 assert_ne!(weak1, weak3);
225 }
226
227 #[test]
229 fn test_weak_ref_upgrade_interface() {
230 let heap = GcHeap::new(&GC_TYPE_REGISTRY);
231 let weak_ref = GcWeak::<TestData1>::new(0, 1);
232
233 let result = weak_ref.upgrade(&heap);
235
236 debug_assert!(result.is_none());
238 }
239
240 #[test]
242 fn test_weak_ref_type_safety() {
243 let weak_i32 = GcWeak::<TestData1>::new(1, 1);
245 let weak_string = GcWeak::<TestData2>::new(1, 1);
246
247 debug_assert_eq!(weak_i32.index(), weak_string.index());
249 debug_assert_eq!(
251 std::any::TypeId::of::<GcWeak<TestData1>>(),
252 std::any::TypeId::of::<GcWeak<TestData1>>()
253 );
254 debug_assert_ne!(
255 std::any::TypeId::of::<GcWeak<TestData1>>(),
256 std::any::TypeId::of::<GcWeak<TestData2>>()
257 );
258 }
259
260 #[test]
262 fn test_weak_ref_edge_cases() {
263 let weak_max = GcWeak::<TestData1>::new(u16::MAX, 1);
265
266 debug_assert_eq!(weak_max.index(), u16::MAX);
267
268 let weak_min = GcWeak::<TestData1>::new(0, 1);
270 debug_assert_eq!(weak_min.index(), 0);
271
272 let weak_i32 = GcWeak::<TestData1>::new(5, 1);
274 let weak_string = GcWeak::<TestData2>::new(5, 1);
275
276 debug_assert_eq!(weak_i32.index(), weak_string.index());
278 }
279
280 #[test]
282 fn test_weak_ref_serialization_compatibility() {
283 let _weak_ref = GcWeak::<TestData1>::new(42, 1);
287
288 assert_eq!(
290 std::mem::size_of::<GcWeak<TestData1>>(),
291 std::mem::size_of::<u32>()
292 );
293
294 assert_eq!(
296 std::mem::align_of::<GcWeak<TestData1>>(),
297 std::mem::align_of::<u32>()
298 );
299 }
300}