use crate::header::GcHeader;
use std::marker::PhantomData;
#[repr(transparent)]
pub struct GcPtr<T> {
ptr: *mut T,
_marker: PhantomData<T>,
}
impl<T> Copy for GcPtr<T> {}
impl<T> Clone for GcPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> GcPtr<T> {
#[inline(always)]
pub unsafe fn from_raw(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null(), "GcPtr::from_raw called with null pointer");
Self {
ptr,
_marker: PhantomData,
}
}
#[inline(always)]
pub fn as_ptr(self) -> *mut T {
self.ptr
}
#[inline(always)]
pub fn as_usize(self) -> usize {
self.ptr as usize
}
#[inline(always)]
pub fn header(self) -> &'static GcHeader {
unsafe {
let header_ptr =
(self.ptr as *const u8).sub(std::mem::size_of::<GcHeader>()) as *const GcHeader;
&*header_ptr
}
}
#[inline(always)]
pub unsafe fn header_mut(self) -> &'static mut GcHeader {
unsafe {
let header_ptr =
(self.ptr as *mut u8).sub(std::mem::size_of::<GcHeader>()) as *mut GcHeader;
&mut *header_ptr
}
}
#[inline(always)]
pub unsafe fn deref_gc(self) -> &'static T {
unsafe { &*self.ptr }
}
#[inline(always)]
pub unsafe fn deref_gc_mut(self) -> &'static mut T {
unsafe { &mut *self.ptr }
}
const MARK_BIT_ARM64: usize = 56;
const MARK_BIT_X86_LAM: usize = 57;
#[cfg(target_arch = "aarch64")]
#[inline(always)]
pub fn with_mark_bit(self) -> Self {
Self {
ptr: (self.ptr as usize | (1 << Self::MARK_BIT_ARM64)) as *mut T,
_marker: PhantomData,
}
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
pub fn clear_mark_bit(self) -> Self {
Self {
ptr: (self.ptr as usize & !(1 << Self::MARK_BIT_ARM64)) as *mut T,
_marker: PhantomData,
}
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
pub fn has_mark_bit(self) -> bool {
(self.ptr as usize & (1 << Self::MARK_BIT_ARM64)) != 0
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
pub fn with_mark_bit(self) -> Self {
if crate::platform::has_x86_lam() {
Self {
ptr: (self.ptr as usize | (1 << Self::MARK_BIT_X86_LAM)) as *mut T,
_marker: PhantomData,
}
} else {
self
}
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
pub fn clear_mark_bit(self) -> Self {
if crate::platform::has_x86_lam() {
Self {
ptr: (self.ptr as usize & !(1 << Self::MARK_BIT_X86_LAM)) as *mut T,
_marker: PhantomData,
}
} else {
self
}
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
pub fn has_mark_bit(self) -> bool {
if crate::platform::has_x86_lam() {
(self.ptr as usize & (1 << Self::MARK_BIT_X86_LAM)) != 0
} else {
false
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
#[inline(always)]
pub fn with_mark_bit(self) -> Self {
self
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
#[inline(always)]
pub fn clear_mark_bit(self) -> Self {
self
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
#[inline(always)]
pub fn has_mark_bit(self) -> bool {
false
}
#[inline(always)]
pub fn is_marked(self) -> bool {
if self.has_mark_bit() {
return true;
}
let header = self.header();
header.color() != crate::header::GcColor::White
}
#[inline(always)]
pub fn raw_ptr(self) -> *mut T {
let mode = crate::platform::cached_masking_mode();
crate::platform::mask_ptr(self.ptr as *mut u8, mode) as *mut T
}
#[inline(always)]
pub fn as_untyped(self) -> *mut u8 {
self.ptr as *mut u8
}
#[inline(always)]
pub unsafe fn from_untyped(ptr: *mut u8) -> Self {
Self {
ptr: ptr as *mut T,
_marker: PhantomData,
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for GcPtr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GcPtr({:p})", self.ptr)
}
}
impl<T> std::fmt::Pointer for GcPtr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Pointer::fmt(&self.ptr, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::header::{GcColor, GcHeader};
#[test]
fn test_gc_ptr_is_8_bytes() {
assert_eq!(std::mem::size_of::<GcPtr<u64>>(), 8);
}
#[test]
fn test_gc_ptr_header_access() {
let mut buf = [0u8; 16]; let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe {
header_ptr.write(GcHeader::new(1, 8));
}
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe {
data_ptr.write(42);
}
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
let header = gc_ptr.header();
assert_eq!(header.kind, 1);
assert_eq!(header.size, 8);
let val = unsafe { gc_ptr.deref_gc() };
assert_eq!(*val, 42);
}
#[test]
fn test_mark_bit_initial_state() {
let mut buf = [0u8; 16];
let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe { header_ptr.write(GcHeader::new(0, 8)) };
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe { data_ptr.write(0) };
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
assert!(!gc_ptr.has_mark_bit());
}
#[test]
fn test_mark_bit_set_clear_cycle() {
let mut buf = [0u8; 16];
let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe { header_ptr.write(GcHeader::new(0, 8)) };
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe { data_ptr.write(0xCAFE) };
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
let marked = gc_ptr.with_mark_bit();
let cleared = marked.clear_mark_bit();
#[cfg(target_arch = "x86_64")]
{
if !crate::platform::has_x86_lam() {
assert!(!marked.has_mark_bit());
assert!(!cleared.has_mark_bit());
assert_eq!(gc_ptr.as_ptr(), marked.as_ptr());
assert_eq!(gc_ptr.as_ptr(), cleared.as_ptr());
} else {
assert!(marked.has_mark_bit());
assert!(!cleared.has_mark_bit());
assert_eq!(gc_ptr.as_ptr(), cleared.as_ptr());
}
}
#[cfg(target_arch = "aarch64")]
{
assert!(marked.has_mark_bit());
assert!(!cleared.has_mark_bit());
assert_eq!(gc_ptr.raw_ptr(), marked.raw_ptr());
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
{
assert!(!marked.has_mark_bit());
assert!(!cleared.has_mark_bit());
assert_eq!(gc_ptr.as_ptr(), marked.as_ptr());
}
}
#[test]
fn test_mark_bit_idempotent() {
let mut buf = [0u8; 16];
let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe { header_ptr.write(GcHeader::new(0, 8)) };
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe { data_ptr.write(0) };
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
let marked_once = gc_ptr.with_mark_bit();
let marked_twice = marked_once.with_mark_bit();
assert_eq!(marked_once.as_ptr(), marked_twice.as_ptr());
}
#[test]
fn test_is_marked_uses_header_fallback() {
let mut buf = [0u8; 16];
let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe { header_ptr.write(GcHeader::new(0, 8)) };
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe { data_ptr.write(42) };
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
assert!(!gc_ptr.is_marked());
let header = unsafe { gc_ptr.header_mut() };
header.set_color(GcColor::Gray);
assert!(gc_ptr.is_marked());
header.set_color(GcColor::Black);
assert!(gc_ptr.is_marked());
header.set_color(GcColor::White);
assert!(!gc_ptr.is_marked());
}
#[test]
fn test_raw_ptr_strips_metadata() {
let mut buf = [0u8; 16];
let header_ptr = buf.as_mut_ptr() as *mut GcHeader;
unsafe { header_ptr.write(GcHeader::new(0, 8)) };
let data_ptr = unsafe { buf.as_mut_ptr().add(8) } as *mut u64;
unsafe { data_ptr.write(0) };
let gc_ptr = unsafe { GcPtr::<u64>::from_raw(data_ptr) };
let marked = gc_ptr.with_mark_bit();
let raw = marked.raw_ptr();
assert_eq!(
raw as usize & 0x0000_FFFF_FFFF_FFFF,
data_ptr as usize & 0x0000_FFFF_FFFF_FFFF,
);
}
}