1use crate::header::GcHeader;
20use std::marker::PhantomData;
21
22#[repr(transparent)]
33pub struct GcPtr<T> {
34 ptr: *mut T,
35 _marker: PhantomData<T>,
36}
37
38impl<T> Copy for GcPtr<T> {}
40impl<T> Clone for GcPtr<T> {
41 fn clone(&self) -> Self {
42 *self
43 }
44}
45
46impl<T> GcPtr<T> {
47 #[inline(always)]
52 pub unsafe fn from_raw(ptr: *mut T) -> Self {
53 debug_assert!(!ptr.is_null(), "GcPtr::from_raw called with null pointer");
54 Self {
55 ptr,
56 _marker: PhantomData,
57 }
58 }
59
60 #[inline(always)]
62 pub fn as_ptr(self) -> *mut T {
63 self.ptr
64 }
65
66 #[inline(always)]
68 pub fn as_usize(self) -> usize {
69 self.ptr as usize
70 }
71
72 #[inline(always)]
74 pub fn header(self) -> &'static GcHeader {
75 unsafe {
76 let header_ptr =
77 (self.ptr as *const u8).sub(std::mem::size_of::<GcHeader>()) as *const GcHeader;
78 &*header_ptr
79 }
80 }
81
82 #[inline(always)]
87 pub unsafe fn header_mut(self) -> &'static mut GcHeader {
88 unsafe {
89 let header_ptr =
90 (self.ptr as *mut u8).sub(std::mem::size_of::<GcHeader>()) as *mut GcHeader;
91 &mut *header_ptr
92 }
93 }
94
95 #[inline(always)]
100 pub unsafe fn deref_gc(self) -> &'static T {
101 unsafe { &*self.ptr }
102 }
103
104 #[inline(always)]
109 pub unsafe fn deref_gc_mut(self) -> &'static mut T {
110 unsafe { &mut *self.ptr }
111 }
112
113 const MARK_BIT_ARM64: usize = 56;
117
118 const MARK_BIT_X86_LAM: usize = 57;
120
121 #[cfg(target_arch = "aarch64")]
127 #[inline(always)]
128 pub fn with_mark_bit(self) -> Self {
129 Self {
131 ptr: (self.ptr as usize | (1 << Self::MARK_BIT_ARM64)) as *mut T,
132 _marker: PhantomData,
133 }
134 }
135
136 #[cfg(target_arch = "aarch64")]
138 #[inline(always)]
139 pub fn clear_mark_bit(self) -> Self {
140 Self {
141 ptr: (self.ptr as usize & !(1 << Self::MARK_BIT_ARM64)) as *mut T,
142 _marker: PhantomData,
143 }
144 }
145
146 #[cfg(target_arch = "aarch64")]
148 #[inline(always)]
149 pub fn has_mark_bit(self) -> bool {
150 (self.ptr as usize & (1 << Self::MARK_BIT_ARM64)) != 0
151 }
152
153 #[cfg(target_arch = "x86_64")]
155 #[inline(always)]
156 pub fn with_mark_bit(self) -> Self {
157 if crate::platform::has_x86_lam() {
158 Self {
159 ptr: (self.ptr as usize | (1 << Self::MARK_BIT_X86_LAM)) as *mut T,
160 _marker: PhantomData,
161 }
162 } else {
163 self
165 }
166 }
167
168 #[cfg(target_arch = "x86_64")]
170 #[inline(always)]
171 pub fn clear_mark_bit(self) -> Self {
172 if crate::platform::has_x86_lam() {
173 Self {
174 ptr: (self.ptr as usize & !(1 << Self::MARK_BIT_X86_LAM)) as *mut T,
175 _marker: PhantomData,
176 }
177 } else {
178 self
179 }
180 }
181
182 #[cfg(target_arch = "x86_64")]
184 #[inline(always)]
185 pub fn has_mark_bit(self) -> bool {
186 if crate::platform::has_x86_lam() {
187 (self.ptr as usize & (1 << Self::MARK_BIT_X86_LAM)) != 0
188 } else {
189 false
191 }
192 }
193
194 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
196 #[inline(always)]
197 pub fn with_mark_bit(self) -> Self {
198 self
200 }
201
202 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
204 #[inline(always)]
205 pub fn clear_mark_bit(self) -> Self {
206 self
207 }
208
209 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
211 #[inline(always)]
212 pub fn has_mark_bit(self) -> bool {
213 false
214 }
215
216 #[inline(always)]
222 pub fn is_marked(self) -> bool {
223 if self.has_mark_bit() {
224 return true;
225 }
226 let header = self.header();
228 header.color() != crate::header::GcColor::White
229 }
230
231 #[inline(always)]
235 pub fn raw_ptr(self) -> *mut T {
236 let mode = crate::platform::cached_masking_mode();
237 crate::platform::mask_ptr(self.ptr as *mut u8, mode) as *mut T
238 }
239
240 #[inline(always)]
242 pub fn as_untyped(self) -> *mut u8 {
243 self.ptr as *mut u8
244 }
245
246 #[inline(always)]
251 pub unsafe fn from_untyped(ptr: *mut u8) -> Self {
252 Self {
253 ptr: ptr as *mut T,
254 _marker: PhantomData,
255 }
256 }
257}
258
259impl<T: std::fmt::Debug> std::fmt::Debug for GcPtr<T> {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(f, "GcPtr({:p})", self.ptr)
262 }
263}
264
265impl<T> std::fmt::Pointer for GcPtr<T> {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 std::fmt::Pointer::fmt(&self.ptr, f)
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::header::{GcColor, GcHeader};
275
276 #[test]
277 fn test_gc_ptr_is_8_bytes() {
278 assert_eq!(std::mem::size_of::<GcPtr<u64>>(), 8);
279 }
280
281 #[test]
282 fn test_gc_ptr_header_access() {
283 let mut buf = [0u8; 16]; let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
286 unsafe {
287 header_ptr.write(GcHeader::new(1, 8));
288 }
289 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
290 unsafe {
291 data_ptr.write(42);
292 }
293
294 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
295 let header = gc_ptr.header();
296 assert_eq!(header.kind, 1);
297 assert_eq!(header.size, 8);
298
299 let val = unsafe { gc_ptr.deref_gc() };
300 assert_eq!(*val, 42);
301 }
302
303 #[test]
306 fn test_mark_bit_initial_state() {
307 let mut buf = [0u8; 16];
310 let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
311 unsafe { header_ptr.write(GcHeader::new(0, 8)) };
312 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
313 unsafe { data_ptr.write(0) };
314
315 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
316 assert!(!gc_ptr.has_mark_bit());
317 }
318
319 #[test]
320 fn test_mark_bit_set_clear_cycle() {
321 let mut buf = [0u8; 16];
322 let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
323 unsafe { header_ptr.write(GcHeader::new(0, 8)) };
324 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
325 unsafe { data_ptr.write(0xCAFE) };
326
327 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
328
329 let marked = gc_ptr.with_mark_bit();
331 let cleared = marked.clear_mark_bit();
333
334 #[cfg(target_arch = "x86_64")]
337 {
338 if !crate::platform::has_x86_lam() {
339 assert!(!marked.has_mark_bit());
341 assert!(!cleared.has_mark_bit());
342 assert_eq!(gc_ptr.as_ptr(), marked.as_ptr());
343 assert_eq!(gc_ptr.as_ptr(), cleared.as_ptr());
344 } else {
345 assert!(marked.has_mark_bit());
347 assert!(!cleared.has_mark_bit());
348 assert_eq!(gc_ptr.as_ptr(), cleared.as_ptr());
350 }
351 }
352
353 #[cfg(target_arch = "aarch64")]
354 {
355 assert!(marked.has_mark_bit());
357 assert!(!cleared.has_mark_bit());
358 assert_eq!(gc_ptr.raw_ptr(), marked.raw_ptr());
360 }
361
362 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
364 {
365 assert!(!marked.has_mark_bit());
366 assert!(!cleared.has_mark_bit());
367 assert_eq!(gc_ptr.as_ptr(), marked.as_ptr());
368 }
369 }
370
371 #[test]
372 fn test_mark_bit_idempotent() {
373 let mut buf = [0u8; 16];
374 let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
375 unsafe { header_ptr.write(GcHeader::new(0, 8)) };
376 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
377 unsafe { data_ptr.write(0) };
378
379 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
380 let marked_once = gc_ptr.with_mark_bit();
381 let marked_twice = marked_once.with_mark_bit();
382 assert_eq!(marked_once.as_ptr(), marked_twice.as_ptr());
384 }
385
386 #[test]
387 fn test_is_marked_uses_header_fallback() {
388 let mut buf = [0u8; 16];
391 let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
392 unsafe { header_ptr.write(GcHeader::new(0, 8)) };
393 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
394 unsafe { data_ptr.write(42) };
395
396 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
397
398 assert!(!gc_ptr.is_marked());
400
401 let header = unsafe { gc_ptr.header_mut() };
403 header.set_color(GcColor::Gray);
404 assert!(gc_ptr.is_marked());
405
406 header.set_color(GcColor::Black);
408 assert!(gc_ptr.is_marked());
409
410 header.set_color(GcColor::White);
412 assert!(!gc_ptr.is_marked());
413 }
414
415 #[test]
416 fn test_raw_ptr_strips_metadata() {
417 let mut buf = [0u8; 16];
418 let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
419 unsafe { header_ptr.write(GcHeader::new(0, 8)) };
420 let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
421 unsafe { data_ptr.write(0) };
422
423 let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
424 let marked = gc_ptr.with_mark_bit();
425
426 let raw = marked.raw_ptr();
429 assert_eq!(
431 raw as usize & 0x0000_FFFF_FFFF_FFFF,
432 data_ptr as usize & 0x0000_FFFF_FFFF_FFFF,
433 );
434 }
435}