1use std::{cell::Cell, marker::PhantomData};
5
6use crate::{Gc, 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 #[inline(always)]
43 fn clone(&self) -> Self {
44 *self
45 }
46}
47
48impl<T: GcNode> Copy for GcWeak<T> {}
49
50impl<T: GcNode> Default for GcWeak<T> {
51 fn default() -> Self {
53 Self {
54 weak_id: GcWeakRawId::NULL,
55 _marker: PhantomData,
56 }
57 }
58}
59
60impl<T: GcNode> std::fmt::Debug for GcWeak<T> {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(f, "GcWeak({}#{})", self.index(), self.version())
63 }
64}
65
66impl<T: GcNode> GcWeak<T> {
67 pub(crate) fn new(index: u16, version: u16) -> Self {
68 debug_assert!(version > 0, "GcWeak version must be non-zero");
69 Self {
70 weak_id: GcWeakRawId(((index as u32) << 16) | (version as u32)),
71 _marker: PhantomData,
72 }
73 }
74
75 pub(crate) fn from_id(weak_id: GcWeakRawId) -> Self {
76 Self {
77 weak_id,
78 _marker: PhantomData,
79 }
80 }
81
82 #[inline(always)]
84 pub fn index(&self) -> u16 {
85 self.weak_id.index()
86 }
87
88 #[inline(always)]
89 pub fn version(&self) -> u16 {
90 self.weak_id.version()
91 }
92
93 #[inline(always)]
95 pub fn upgrade<'a>(&self, heap: &'a GcHeap) -> Option<Gc<'a, T>> {
96 heap.upgrade(self)
97 }
98}
99
100impl GcHeap {
101 pub fn downgrade<T: GcNode>(&mut self, gc_ref: &GcRef<T>) -> GcWeak<T> {
103 let node = unsafe {
104 let mut h = gc_ref.head_ptr;
105 h.as_mut()
106 };
107
108 if !node.weak_id.is_null() {
109 #[cfg(debug_assertions)]
111 {
112 debug_assert!((node.weak_id.index() as usize) < self.weak_slots.len());
113 let slot = &self.weak_slots[node.weak_id.index() as usize];
114 let ver = slot.0;
115 let ptr = slot.1.get();
116 debug_assert!(ver == node.weak_id.version() && ptr == Some(gc_ref.head_ptr));
117 }
118 GcWeak::from_id(node.weak_id)
119 } else {
120 if self.weak_slots.len() == u16::MAX as usize {
121 panic!("too may weakrefs");
122 }
123
124 let i = self
126 .weak_slots
127 .iter()
128 .position(|(_, slot)| slot.get().is_none())
129 .unwrap_or_else(|| {
130 let n = self.weak_slots.len();
131 self.weak_slots.push((0, Cell::new(None)));
132 n
133 });
134
135 unsafe {
136 let curr_ver = self.weak_slots.get_unchecked(i).0;
138 let version = if curr_ver == u16::MAX {
139 1
140 } else {
141 curr_ver + 1
142 };
143
144 let weak = GcWeak::new(i as _, version);
145 let slot = self.weak_slots.get_unchecked_mut(i);
146 slot.0 = version;
147 slot.1.set(Some(gc_ref.head_ptr));
148 node.weak_id = weak.weak_id;
149
150 weak
151 }
152 }
153 }
154
155 pub fn upgrade<'a, T: GcNode>(&'a self, weak_ref: &GcWeak<T>) -> Option<Gc<'a, T>> {
157 if !weak_ref.weak_id.is_null() {
158 self.weak_slots
159 .get(weak_ref.index() as usize)
160 .and_then(|(version, node)| {
161 if *version == weak_ref.version() {
162 node.get()
163 } else {
164 None
165 }
166 })
167 .map(|ptr| {
168 unsafe {
171 Gc::from_raw(GcRef {
172 head_ptr: ptr,
173 _marker: PhantomData,
174 })
175 }
176 })
177 } else {
178 None
179 }
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use crate::{GcTrace, GcTraceCtx, gc_type_register};
186
187 use super::*;
188
189 #[derive(Debug, Default, PartialEq, Eq)]
190 struct TestData1;
191
192 impl GcTrace for TestData1 {
193 fn trace(&self, _: &mut GcTraceCtx) {}
194 }
195
196 #[derive(Debug, Default, PartialEq, Eq)]
197 struct TestData2;
198
199 impl GcTrace for TestData2 {
200 fn trace(&self, _: &mut GcTraceCtx) {}
201 }
202
203 gc_type_register! {
204 TestData1;
205 TestData2;
206 }
207
208 #[test]
210 fn test_weak_ref_clone_and_copy() {
211 let weak1 = GcWeak::<TestData1>::new(10, 1);
212
213 let weak2 = weak1;
215 assert_eq!(weak1.index(), weak2.index());
216
217 let weak3 = weak1;
219 let weak4 = weak1; assert_eq!(weak3.index(), weak4.index());
221 }
222
223 #[test]
225 fn test_weak_ref_equality() {
226 let weak1 = GcWeak::<TestData1>::new(5, 1);
227 let weak2 = GcWeak::<TestData1>::new(5, 1);
228 let weak3 = GcWeak::<TestData1>::new(10, 1);
229
230 assert_eq!(weak1, weak2);
232
233 assert_ne!(weak1, weak3);
235 }
236
237 #[test]
239 fn test_weak_ref_upgrade_interface() {
240 let heap = GcHeap::new(&GC_TYPE_REGISTRY);
241 let weak_ref = GcWeak::<TestData1>::new(0, 1);
242
243 let result = weak_ref.upgrade(&heap);
245
246 debug_assert!(result.is_none());
248 }
249
250 #[test]
252 fn test_weak_ref_type_safety() {
253 let weak_i32 = GcWeak::<TestData1>::new(1, 1);
255 let weak_string = GcWeak::<TestData2>::new(1, 1);
256
257 debug_assert_eq!(weak_i32.index(), weak_string.index());
259 debug_assert_eq!(
261 std::any::TypeId::of::<GcWeak<TestData1>>(),
262 std::any::TypeId::of::<GcWeak<TestData1>>()
263 );
264 debug_assert_ne!(
265 std::any::TypeId::of::<GcWeak<TestData1>>(),
266 std::any::TypeId::of::<GcWeak<TestData2>>()
267 );
268 }
269
270 #[test]
272 fn test_weak_ref_edge_cases() {
273 let weak_max = GcWeak::<TestData1>::new(u16::MAX, 1);
275
276 debug_assert_eq!(weak_max.index(), u16::MAX);
277
278 let weak_min = GcWeak::<TestData1>::new(0, 1);
280 debug_assert_eq!(weak_min.index(), 0);
281
282 let weak_i32 = GcWeak::<TestData1>::new(5, 1);
284 let weak_string = GcWeak::<TestData2>::new(5, 1);
285
286 debug_assert_eq!(weak_i32.index(), weak_string.index());
288 }
289
290 #[test]
292 fn test_weak_ref_serialization_compatibility() {
293 let _weak_ref = GcWeak::<TestData1>::new(42, 1);
297
298 assert_eq!(
300 std::mem::size_of::<GcWeak<TestData1>>(),
301 std::mem::size_of::<u32>()
302 );
303
304 assert_eq!(
306 std::mem::align_of::<GcWeak<TestData1>>(),
307 std::mem::align_of::<u32>()
308 );
309 }
310}