use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
#[cfg(feature = "pointer-compression")]
use crate::compression::CompressedPtr;
pub struct GcPtr<T> {
#[cfg(not(feature = "pointer-compression"))]
ptr: *const T,
#[cfg(feature = "pointer-compression")]
ptr: CompressedPtr,
_phantom: PhantomData<T>,
}
impl<T> GcPtr<T> {
pub unsafe fn from_raw(ptr: *const T) -> Self {
Self {
#[cfg(not(feature = "pointer-compression"))]
ptr,
#[cfg(feature = "pointer-compression")]
ptr: CompressedPtr::compress(ptr as *const u8),
_phantom: PhantomData,
}
}
pub unsafe fn as_raw(&self) -> *const T {
#[cfg(not(feature = "pointer-compression"))]
{
self.ptr
}
#[cfg(feature = "pointer-compression")]
{
self.ptr.decompress() as *const T
}
}
pub unsafe fn as_raw_mut(&self) -> *mut T {
self.as_raw() as *mut T
}
pub fn is_null(&self) -> bool {
#[cfg(not(feature = "pointer-compression"))]
{
self.ptr.is_null()
}
#[cfg(feature = "pointer-compression")]
{
self.ptr.is_null()
}
}
pub fn null() -> Self {
Self {
#[cfg(not(feature = "pointer-compression"))]
ptr: std::ptr::null(),
#[cfg(feature = "pointer-compression")]
ptr: CompressedPtr::null(),
_phantom: PhantomData,
}
}
pub unsafe fn cast<U>(&self) -> GcPtr<U> {
GcPtr {
#[cfg(not(feature = "pointer-compression"))]
ptr: self.ptr as *const U,
#[cfg(feature = "pointer-compression")]
ptr: self.ptr,
_phantom: PhantomData,
}
}
}
impl<T> Deref for GcPtr<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.as_raw() }
}
}
impl<T> DerefMut for GcPtr<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.as_raw_mut() }
}
}
impl<T> Clone for GcPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for GcPtr<T> {}
impl<T: PartialEq> PartialEq for GcPtr<T> {
fn eq(&self, other: &Self) -> bool {
if self.is_null() && other.is_null() {
true
} else if self.is_null() || other.is_null() {
false
} else {
**self == **other
}
}
}
impl<T: Eq> Eq for GcPtr<T> {}
impl<T: Hash> Hash for GcPtr<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
if !self.is_null() {
(**self).hash(state);
}
}
}
impl<T: fmt::Debug> fmt::Debug for GcPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_null() {
write!(f, "GcPtr(null)")
} else {
write!(f, "GcPtr({:?})", **self)
}
}
}
impl<T: fmt::Display> fmt::Display for GcPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_null() {
write!(f, "null")
} else {
write!(f, "{}", **self)
}
}
}
unsafe impl<T: Send> Send for GcPtr<T> {}
unsafe impl<T: Sync> Sync for GcPtr<T> {}
pub struct WeakGcPtr<T> {
#[cfg(not(feature = "pointer-compression"))]
ptr: *const T,
#[cfg(feature = "pointer-compression")]
ptr: CompressedPtr,
_phantom: PhantomData<T>,
}
impl<T> WeakGcPtr<T> {
pub fn from_gc_ptr(ptr: &GcPtr<T>) -> Self {
Self {
#[cfg(not(feature = "pointer-compression"))]
ptr: ptr.ptr,
#[cfg(feature = "pointer-compression")]
ptr: ptr.ptr,
_phantom: PhantomData,
}
}
pub fn upgrade(&self) -> Option<GcPtr<T>> {
unsafe {
#[cfg(not(feature = "pointer-compression"))]
let raw_ptr = self.ptr;
#[cfg(feature = "pointer-compression")]
let raw_ptr = self.ptr.decompress() as *const T;
if raw_ptr.is_null() {
None
} else {
Some(GcPtr::from_raw(raw_ptr))
}
}
}
pub fn is_expired(&self) -> bool {
self.upgrade().is_none()
}
}
impl<T> Clone for WeakGcPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for WeakGcPtr<T> {}
pub trait IntoGcPtr<T> {
fn into_gc_ptr(self) -> crate::Result<GcPtr<T>>;
}
#[cfg(test)]
mod tests {
use super::*;
use runmat_builtins::Value;
#[test]
fn test_gc_ptr_null() {
let ptr: GcPtr<Value> = GcPtr::null();
assert!(ptr.is_null());
}
#[test]
fn test_gc_ptr_clone() {
let ptr: GcPtr<Value> = GcPtr::null();
let ptr2 = ptr;
assert_eq!(ptr.is_null(), ptr2.is_null());
}
#[test]
fn test_weak_gc_ptr() {
let ptr: GcPtr<Value> = GcPtr::null();
let weak = WeakGcPtr::from_gc_ptr(&ptr);
assert!(weak.is_expired());
}
}