cycle_ptr 0.1.0

Smart pointers, with cycles
//! Implement user-facing type to refer to a generation.
use crate::generation::{Generation, GenerationPtr};
use crate::object::Object;
use crate::object::metadata::Metadata;
use crate::pointer::GcPtr;
use std::fmt;

#[cfg(feature = "weak_pointer")]
use crate::pointer::Weak;

/// A reference to a generation.
/// Generations are used to track object liveness.
///
/// Multiple generations can work independently of eachother,
/// as long as the objects in the younger generation don't point at objects in the older generation.
#[derive(Clone)]
pub struct GenerationRef {
    /// The actual generation pointer as it is used internally by the library.
    ptr: GenerationPtr,
}

impl GenerationRef {
    /// Create a new generation reference for a specific generation.
    #[inline]
    pub(crate) const fn new_from_ptr(ptr: GenerationPtr) -> GenerationRef {
        GenerationRef { ptr }
    }

    /// Create new object and return the [GcPtr] to it.
    #[inline]
    pub fn make<T, Factory>(&self, factory: Factory) -> GcPtr<T>
    where
        T: 'static,
        Factory: FnOnce(Metadata) -> T,
    {
        // SAFETY: we create the [Object] with a reference count of 1.
        unsafe { GcPtr::new_from_raw(Object::new_ptr(1, factory, Some(self.ptr.clone()))) }
    }

    /// Create new object and return the [GcPtr] to it.
    #[cfg(feature = "weak_pointer")]
    #[inline]
    pub fn make_cyclic<T, Factory>(&self, factory: Factory) -> GcPtr<T>
    where
        T: 'static,
        Factory: FnOnce(Metadata, Weak<T>) -> T,
    {
        // SAFETY: we create the [Object] with a reference count of 1.
        unsafe { GcPtr::new_from_raw(Object::new_cyclic_ptr(1, factory, Some(self.ptr.clone()))) }
    }
}

impl Default for GenerationRef {
    #[allow(
        clippy::missing_inline_in_public_items,
        reason = "Always performs an allocation."
    )]
    fn default() -> Self {
        Self::new_from_ptr(Generation::new())
    }
}

impl PartialEq for GenerationRef {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        GenerationPtr::ptr_eq(&self.ptr, &other.ptr)
    }
}

impl Eq for GenerationRef {}

impl fmt::Debug for GenerationRef {
    #[allow(
        clippy::missing_inline_in_public_items,
        reason = "Keeps the generation internals nicely hidden, and also if I messed up and we get into an infinite recursion, seeing this in the stack trace will be helpful."
    )]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        fmt::Debug::fmt(&self.ptr, f)
    }
}

/// Implement the threadsafe counterpart of this module.
#[cfg(feature = "multi_thread")]
pub(crate) mod sync {
    use crate::generation::{MTGeneration, MTGenerationPtr};
    use crate::object::MTObject;
    use crate::object::metadata::sync::Metadata;
    use crate::pointer::sync::GcMtPtr;
    use std::fmt;

    #[cfg(feature = "weak_pointer")]
    use crate::pointer::sync::Weak;

    /// A reference to a generation.
    /// Generations are used to track object liveness.
    ///
    /// Multiple generations can work independently of eachother,
    /// as long as the objects in the younger generation don't point at objects in the older generation.
    #[derive(Clone)]
    pub struct GenerationRef {
        /// The actual generation pointer as it is used internally by the library.
        ptr: MTGenerationPtr,
    }

    impl GenerationRef {
        /// Create a new generation reference for a specific generation.
        #[inline]
        pub(crate) const fn new_from_ptr(ptr: MTGenerationPtr) -> GenerationRef {
            GenerationRef { ptr }
        }

        /// Create new object and return the [GcMtPtr] to it.
        #[inline]
        pub fn make<T, Factory>(&self, factory: Factory) -> GcMtPtr<T>
        where
            T: 'static + Send + Sync,
            Factory: FnOnce(Metadata) -> T,
        {
            // SAFETY: we create the [Object] with a reference count of 1.
            unsafe { GcMtPtr::new_from_raw(MTObject::new_ptr(1, factory, Some(self.ptr.clone()))) }
        }

        /// Create new object and return the [GcMtPtr] to it.
        #[cfg(feature = "weak_pointer")]
        #[inline]
        pub fn make_cyclic<T, Factory>(&self, factory: Factory) -> GcMtPtr<T>
        where
            T: 'static + Send + Sync,
            Factory: FnOnce(Metadata, Weak<T>) -> T,
        {
            // SAFETY: we create the [Object] with a reference count of 1.
            unsafe {
                GcMtPtr::new_from_raw(MTObject::new_cyclic_ptr(1, factory, Some(self.ptr.clone())))
            }
        }
    }

    impl Default for GenerationRef {
        #[allow(
            clippy::missing_inline_in_public_items,
            reason = "Always performs an allocation."
        )]
        fn default() -> Self {
            Self::new_from_ptr(MTGeneration::new())
        }
    }

    impl PartialEq for GenerationRef {
        #[inline]
        fn eq(&self, other: &Self) -> bool {
            MTGenerationPtr::ptr_eq(&self.ptr, &other.ptr)
        }
    }

    impl Eq for GenerationRef {}

    impl fmt::Debug for GenerationRef {
        #[allow(
            clippy::missing_inline_in_public_items,
            reason = "Keeps the generation internals nicely hidden, and also if I messed up and we get into an infinite recursion, seeing this in the stack trace will be helpful."
        )]
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
            fmt::Debug::fmt(&self.ptr, f)
        }
    }
}