use crate::Finalize;
use crate::collector::{GarbageCollector, Trace};
use crate::error::{Error, Result};
use crate::pointers::SafePtr;
use crate::root_guard::GcRootGuard;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::size_of;
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::sync::Arc;
pub struct Gc<T> {
pub(crate) ptr: NonNull<T>,
pub(crate) phantom: PhantomData<T>,
}
impl<T> Gc<T> {
fn allocate(collector: &GarbageCollector, data: T) -> (Self, *mut T, usize) {
let ptr = NonNull::from(Box::leak(Box::new(data)));
let size = size_of::<T>();
collector.record_allocation(size);
let gc = Self {
ptr,
phantom: PhantomData,
};
(gc, ptr.as_ptr(), size)
}
#[expect(clippy::new_ret_no_self)]
pub fn new(collector: &Arc<GarbageCollector>, data: T) -> GcRootGuard<T>
where
T: Send + Sync + Trace,
{
Self::with_collector(collector, data)
}
pub unsafe fn new_unrooted(collector: &GarbageCollector, data: T) -> Self
where
T: Send + Sync,
{
unsafe { Self::with_collector_unrooted(collector, data) }
}
pub fn new_with_finalizer(collector: &Arc<GarbageCollector>, data: T) -> GcRootGuard<T>
where
T: Send + Sync + Finalize + Trace,
{
Self::with_collector_and_finalizer(collector, data)
}
pub fn with_collector(collector: &Arc<GarbageCollector>, data: T) -> GcRootGuard<T>
where
T: Send + Sync + Trace,
{
let (gc, ptr, size) = Self::allocate(collector, data);
let root_guard = collector.create_root_guard(gc);
collector.register_object::<T>(ptr, size);
root_guard
}
pub unsafe fn with_collector_unrooted(collector: &GarbageCollector, data: T) -> Self
where
T: Send + Sync,
{
let (gc, ptr, size) = Self::allocate(collector, data);
collector.register_object::<T>(ptr, size);
gc
}
pub fn with_collector_and_finalizer(
collector: &Arc<GarbageCollector>,
data: T,
) -> GcRootGuard<T>
where
T: Send + Sync + Finalize + Trace,
{
let (gc, ptr, size) = Self::allocate(collector, data);
let root_guard = collector.create_root_guard(gc);
collector.register_object_with_finalizer::<T>(ptr, size);
root_guard
}
pub unsafe fn with_collector_and_finalizer_unrooted(
collector: &GarbageCollector,
data: T,
) -> Self
where
T: Send + Sync + Finalize,
{
let (gc, ptr, size) = Self::allocate(collector, data);
collector.register_object_with_finalizer::<T>(ptr, size);
gc
}
#[must_use]
pub fn ptr_eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
#[must_use]
pub fn as_ptr(&self) -> *const T {
ptr::addr_of!(**self)
}
#[must_use]
pub fn as_ptr_i64(&self) -> i64 {
self.as_ptr() as i64
}
pub unsafe fn from_raw(ptr: *const T) -> Result<Self> {
let ptr = NonNull::new(ptr.cast_mut()).ok_or_else(|| {
Error::InvalidPointer("Gc::from_raw received null pointer".to_string())
})?;
Ok(Self {
ptr,
phantom: PhantomData,
})
}
pub fn from_raw_i64(ptr: i64) -> Result<Self> {
let address = usize::try_from(ptr).map_err(|_| {
Error::InvalidPointer(format!("Gc::from_raw_i64 received negative pointer {ptr}"))
})?;
let raw = address as *const T;
unsafe { Self::from_raw(raw) }
}
#[must_use]
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
pub(crate) fn inner(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
pub fn as_root(&self, collector: &Arc<GarbageCollector>) -> Result<GcRootGuard<T>>
where
T: Trace,
{
Ok(collector.create_root_guard(self.clone()))
}
pub fn write_barrier(&self, collector: &GarbageCollector)
where
T: Trace,
{
collector.write_barrier(self);
}
}
impl<T> Clone for Gc<T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
phantom: PhantomData,
}
}
}
impl<T> Deref for Gc<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner()
}
}
impl<T> Drop for Gc<T> {
fn drop(&mut self) {
}
}
unsafe impl<T: Sync + Send> Send for Gc<T> {}
unsafe impl<T: Sync + Send> Sync for Gc<T> {}
impl<T: fmt::Display> fmt::Display for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: fmt::Debug> fmt::Debug for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> fmt::Pointer for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr(), f)
}
}
impl<T: PartialEq> PartialEq for Gc<T> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: Eq> Eq for Gc<T> {}
impl<T: PartialOrd> PartialOrd for Gc<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: Ord> Ord for Gc<T> {
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: Hash> Hash for Gc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T> Borrow<T> for Gc<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T> AsRef<T> for Gc<T> {
fn as_ref(&self) -> &T {
self
}
}
impl<T: Trace> Trace for Gc<T> {
fn trace(&self, collector: &GarbageCollector) {
let ptr = SafePtr::from_ptr(self.ptr.as_ptr().cast::<u8>());
if collector.try_mark_object(ptr) {
(**self).trace(collector);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::{HashMap, HashSet};
#[test]
fn test_creation_and_access() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, 42);
assert_eq!(**gc, 42);
}
#[test]
fn test_creation_with_different_types() {
let collector = GarbageCollector::new();
let gc_int = Gc::new(&collector, 123);
let gc_string = Gc::new(&collector, "Hello, World!".to_string());
let gc_vec = Gc::new(&collector, vec![1, 2, 3, 4, 5]);
let gc_tuple = Gc::new(&collector, (1, "test", 1.23));
assert_eq!(**gc_int, 123);
assert_eq!(**gc_string, "Hello, World!");
assert_eq!(**gc_vec, vec![1, 2, 3, 4, 5]);
assert_eq!(**gc_tuple, (1, "test", 1.23));
}
#[test]
fn test_clone() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, 42);
let gc2 = gc1.clone();
assert_eq!(**gc1, 42);
assert_eq!(**gc2, 42);
assert!(Gc::ptr_eq(&gc1, &gc2));
}
#[test]
fn test_multiple_clones() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, "shared data".to_string());
let gc2 = gc1.clone();
let gc3 = gc1.clone();
let gc4 = gc2.clone();
assert!(Gc::ptr_eq(&gc1, &gc2));
assert!(Gc::ptr_eq(&gc1, &gc3));
assert!(Gc::ptr_eq(&gc1, &gc4));
assert_eq!(**gc1, "shared data");
assert_eq!(**gc2, "shared data");
assert_eq!(**gc3, "shared data");
assert_eq!(**gc4, "shared data");
}
#[test]
fn test_drop_behavior() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, vec![1, 2, 3]);
let gc2 = gc1.clone();
drop(gc1);
assert_eq!(**gc2, vec![1, 2, 3]);
}
#[test]
fn test_equality() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, 42);
let gc2 = Gc::new(&collector, 42);
let gc3 = gc1.clone();
assert_eq!(gc1, gc2);
assert_eq!(gc1, gc3);
assert!(Gc::ptr_eq(&gc1, &gc3));
assert!(!Gc::ptr_eq(&gc1, &gc2));
}
#[test]
fn test_with_complex_types() {
let mut map = HashMap::new();
map.insert("key1", 10);
map.insert("key2", 20);
let collector = GarbageCollector::new();
let gc_map = Gc::new(&collector, map);
let gc_map_clone = gc_map.clone();
assert_eq!(gc_map.get("key1"), Some(&10));
assert_eq!(gc_map_clone.get("key2"), Some(&20));
assert!(Gc::ptr_eq(&*gc_map, &*gc_map_clone));
}
#[test]
fn test_as_ptr() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, 42);
let ptr = gc.as_ptr();
unsafe {
assert_eq!(*ptr, 42);
}
}
#[test]
fn test_ptr_eq() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, 42);
let gc2 = Gc::new(&collector, 42);
let gc3 = gc1.clone();
assert!(Gc::ptr_eq(&*gc1, &*gc3)); assert!(!Gc::ptr_eq(&*gc1, &*gc2)); }
#[test]
fn test_borrow() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, "test string".to_string());
let borrowed: &String = gc.borrow();
assert_eq!(borrowed, "test string");
assert_eq!(borrowed.len(), 11);
}
#[test]
fn test_as_ref() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, vec![1, 2, 3, 4, 5]);
let vec_ref: &Vec<i32> = gc.as_ref();
assert_eq!(vec_ref.len(), 5);
assert_eq!(vec_ref[2], 3);
}
#[test]
fn test_from_trait() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, 42);
assert_eq!(**gc, 42);
}
#[test]
fn test_debug_display() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, 42);
let debug_str = format!("{gc:?}");
let display_str = format!("{gc}");
assert_eq!(debug_str, "42");
assert_eq!(display_str, "42");
}
#[test]
fn test_pointer_format() {
let collector = GarbageCollector::new();
let gc = Gc::new(&collector, 42);
let ptr_str = format!("{:p}", &*gc);
assert!(ptr_str.starts_with("0x"));
}
#[test]
fn test_ordering() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, 10);
let gc2 = Gc::new(&collector, 20);
let gc3 = Gc::new(&collector, 10);
assert!(gc1 < gc2);
assert!(gc2 > gc1);
assert_eq!(gc1, gc3);
}
#[test]
#[expect(clippy::mutable_key_type)]
fn test_hash() {
let collector = GarbageCollector::new();
let gc1 = Gc::new(&collector, 42);
let gc2 = Gc::new(&collector, 42);
let gc3 = Gc::new(&collector, 43);
let mut set = HashSet::new();
set.insert(gc1.clone());
set.insert(gc2);
set.insert(gc3);
assert_eq!(set.len(), 2);
assert!(set.contains(&gc1));
}
}