#![deny(missing_docs)]
extern crate core;
use core::cell::Cell;
use core::clone::Clone;
use core::cmp::{PartialEq, PartialOrd, Eq, Ord, Ordering};
use core::default::Default;
use core::fmt;
use core::hash::{Hasher, Hash};
use core::mem::forget;
use std::ptr::NonNull;
use core::ops::{Deref, Drop};
use core::option::Option;
use core::option::Option::{Some, None};
use core::ptr;
use core::result::Result;
use core::result::Result::{Ok, Err};
use std::alloc::{dealloc, Layout};
pub mod trace;
pub use trace::{Trace, Tracer};
pub mod collect;
pub use collect::{collect_cycles, number_of_roots_buffered};
mod cc_box_ptr;
use cc_box_ptr::CcBoxPtr;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum Color {
Black,
Gray,
White,
Purple,
#[allow(dead_code)]
Red,
#[allow(dead_code)]
Orange,
}
#[derive(Debug)]
#[doc(hidden)]
pub struct CcBoxData {
strong: Cell<usize>,
weak: Cell<usize>,
buffered: Cell<bool>,
color: Cell<Color>,
}
#[derive(Debug)]
struct CcBox<T: Trace> {
value: T,
data: CcBoxData,
}
pub struct Cc<T: 'static + Trace> {
_ptr: NonNull<CcBox<T>>,
}
impl<T: Trace> Cc<T> {
pub fn new(value: T) -> Cc<T> {
unsafe {
Cc {
_ptr: NonNull::new_unchecked(Box::into_raw(Box::new(CcBox {
value: value,
data: CcBoxData {
strong: Cell::new(1),
weak: Cell::new(1),
buffered: Cell::new(false),
color: Cell::new(Color::Black),
}
}))),
}
}
}
pub fn downgrade(&self) -> Weak<T> {
self.inc_weak();
Weak { _ptr: self._ptr }
}
}
impl<T: Trace> Cc<T> {
unsafe fn release(&mut self) {
debug_assert!(self.strong() == 0);
self.data().color.set(Color::Black);
if self.buffered() {
return;
}
crate::cc_box_ptr::free(self._ptr);
}
fn possible_root(&mut self) {
debug_assert!(self.strong() > 0);
if self.color() == Color::Purple {
return;
}
self.data().color.set(Color::Purple);
if self.buffered() {
return;
}
self.data().buffered.set(true);
let ptr : NonNull<dyn CcBoxPtr> = self._ptr;
collect::add_root(ptr);
}
}
impl<T: 'static + Trace> Cc<T> {
#[inline]
pub fn is_unique(&self) -> bool {
self.weak_count() == 0 && self.strong_count() == 1
}
#[inline]
pub fn try_unwrap(self) -> Result<T, Cc<T>> {
if self.is_unique() {
unsafe {
let val = ptr::read(&*self);
dealloc(self._ptr.cast().as_ptr(), Layout::new::<CcBox<T>>());
forget(self);
Ok(val)
}
} else {
Err(self)
}
}
#[inline]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.is_unique() {
let inner = unsafe { self._ptr.as_mut() };
Some(&mut inner.value)
} else {
None
}
}
#[inline]
pub fn strong_count(&self) -> usize { self.strong() }
#[inline]
pub fn weak_count(&self) -> usize { self.weak() - 1 }
}
impl<T: 'static + Clone + Trace> Cc<T> {
#[inline]
pub fn make_unique(&mut self) -> &mut T {
if !self.is_unique() {
*self = Cc::new((**self).clone())
}
let inner = unsafe { self._ptr.as_mut() };
&mut inner.value
}
}
impl<T: Trace> Deref for Cc<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
if self.strong_count() > 0 {
unsafe {
&self._ptr.as_ref().value
}
} else {
panic!("Invalid access during cycle collection");
}
}
}
impl<T: Trace> Drop for Cc<T> {
fn drop(&mut self) {
unsafe {
if self.strong() > 0 {
self.dec_strong();
if self.strong() == 0 {
self.release();
} else {
self.possible_root();
}
}
}
}
}
impl<T: Trace> Clone for Cc<T> {
#[inline]
fn clone(&self) -> Cc<T> {
self.inc_strong();
Cc { _ptr: self._ptr }
}
}
impl<T: Default + Trace> Default for Cc<T> {
#[inline]
fn default() -> Cc<T> {
Cc::new(Default::default())
}
}
impl<T: PartialEq + Trace> PartialEq for Cc<T> {
#[inline(always)]
fn eq(&self, other: &Cc<T>) -> bool { **self == **other }
#[inline(always)]
fn ne(&self, other: &Cc<T>) -> bool { **self != **other }
}
impl<T: Eq + Trace> Eq for Cc<T> {}
impl<T: PartialOrd + Trace> PartialOrd for Cc<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Cc<T>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
#[inline(always)]
fn lt(&self, other: &Cc<T>) -> bool { **self < **other }
#[inline(always)]
fn le(&self, other: &Cc<T>) -> bool { **self <= **other }
#[inline(always)]
fn gt(&self, other: &Cc<T>) -> bool { **self > **other }
#[inline(always)]
fn ge(&self, other: &Cc<T>) -> bool { **self >= **other }
}
impl<T: Ord + Trace> Ord for Cc<T> {
#[inline]
fn cmp(&self, other: &Cc<T>) -> Ordering { (**self).cmp(&**other) }
}
impl<T: Hash + Trace> Hash for Cc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: fmt::Display + Trace> fmt::Display for Cc<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: fmt::Debug + Trace> fmt::Debug for Cc<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: Trace> fmt::Pointer for Cc<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self._ptr, f)
}
}
pub struct Weak<T: Trace> {
_ptr: NonNull<CcBox<T>>,
}
impl<T: Trace> Weak<T> {
pub fn upgrade(&self) -> Option<Cc<T>> {
if self.strong() == 0 {
None
} else {
self.inc_strong();
Some(Cc { _ptr: self._ptr })
}
}
}
impl<T: Trace> Drop for Weak<T> {
fn drop(&mut self) {
unsafe {
if self.weak() > 0 {
self.dec_weak();
if self.weak() == 0 {
dealloc(self._ptr.cast().as_ptr(), Layout::new::<CcBox<T>>())
}
}
}
}
}
impl<T: Trace> Clone for Weak<T> {
#[inline]
fn clone(&self) -> Weak<T> {
self.inc_weak();
Weak { _ptr: self._ptr }
}
}
impl<T: fmt::Debug + Trace> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(Weak)")
}
}
impl<T: Trace> Trace for Cc<T> {
fn trace(&self, tracer: &mut Tracer) {
unsafe {
tracer(self._ptr.as_ref());
}
}
}
impl<T: Trace> Trace for Weak<T> {
fn trace(&self, _tracer: &mut Tracer) {
}
}
impl<T: Trace> Trace for CcBox<T> {
fn trace(&self, tracer: &mut Tracer) {
Trace::trace(&self.value, tracer);
}
}
#[doc(hidden)]
impl<T: Trace> CcBoxPtr for Cc<T> {
#[inline(always)]
fn data(&self) -> &CcBoxData {
unsafe {
&self._ptr.as_ref().data
}
}
}
impl<T: Trace> CcBoxPtr for Weak<T> {
#[inline(always)]
fn data(&self) -> &CcBoxData {
unsafe {
&self._ptr.as_ref().data
}
}
}
impl<T: Trace> CcBoxPtr for CcBox<T> {
#[inline(always)]
fn data(&self) -> &CcBoxData { &self.data }
}
unsafe fn deallocate(ptr: NonNull<dyn CcBoxPtr>) {
dealloc(ptr.cast().as_ptr(), Layout::for_value(ptr.as_ref()));
}
unsafe fn drop_value(ptr: NonNull<dyn CcBoxPtr>) {
ptr::drop_in_place(ptr.as_ptr());
}
#[cfg(test)]
mod tests {
use super::{Cc, Weak, Trace, Tracer};
use std::boxed::Box;
use std::cell::RefCell;
use std::option::Option;
use std::option::Option::{Some, None};
use std::result::Result::{Err, Ok};
use std::mem::drop;
use std::clone::Clone;
use collect::collect_cycles;
#[test]
fn test_clone() {
{
let x = Cc::new(RefCell::new(5));
let y = x.clone();
*x.borrow_mut() = 20;
assert_eq!(*y.borrow(), 20);
}
collect_cycles();
}
#[test]
fn test_simple() {
let x = Cc::new(5);
assert_eq!(*x, 5);
}
#[test]
fn test_simple_clone() {
{
let x = Cc::new(5);
let y = x.clone();
assert_eq!(*x, 5);
assert_eq!(*y, 5);
}
collect_cycles();
}
#[test]
fn test_destructor() {
let x: Cc<Box<_>> = Cc::new(Box::new(5));
assert_eq!(**x, 5);
}
#[test]
fn test_live() {
{
let x = Cc::new(5);
let y = x.downgrade();
assert!(y.upgrade().is_some());
}
collect_cycles();
}
#[test]
fn test_dead() {
let x = Cc::new(5);
let y = x.downgrade();
drop(x);
assert!(y.upgrade().is_none());
}
#[test]
fn weak_self_cyclic() {
{
struct Cycle {
x: RefCell<Option<Weak<Cycle>>>
}
impl Trace for Cycle {
fn trace(&self, _: &mut Tracer) { }
}
let a = Cc::new(Cycle { x: RefCell::new(None) });
let b = a.clone().downgrade();
*a.x.borrow_mut() = Some(b);
}
collect_cycles();
}
#[test]
fn is_unique() {
{
let x = Cc::new(3);
assert!(x.is_unique());
let y = x.clone();
assert!(!x.is_unique());
drop(y);
assert!(x.is_unique());
let w = x.downgrade();
assert!(!x.is_unique());
drop(w);
assert!(x.is_unique());
}
collect_cycles();
}
#[test]
fn test_strong_count() {
{
let a = Cc::new(0u32);
assert!(a.strong_count() == 1);
let w = a.downgrade();
assert!(a.strong_count() == 1);
let b = w.upgrade().expect("upgrade of live rc failed");
assert!(b.strong_count() == 2);
assert!(b.strong_count() == 2);
drop(w);
drop(a);
assert!(b.strong_count() == 1);
let c = b.clone();
assert!(b.strong_count() == 2);
assert!(c.strong_count() == 2);
}
collect_cycles();
}
#[test]
fn test_weak_count() {
{
let a = Cc::new(0u32);
assert!(a.strong_count() == 1);
assert!(a.weak_count() == 0);
let w = a.downgrade();
assert!(a.strong_count() == 1);
assert!(a.weak_count() == 1);
drop(w);
assert!(a.strong_count() == 1);
assert!(a.weak_count() == 0);
let c = a.clone();
assert!(a.strong_count() == 2);
assert!(a.weak_count() == 0);
drop(c);
}
collect_cycles();
}
#[test]
fn try_unwrap() {
{
let x = Cc::new(3);
assert_eq!(x.try_unwrap(), Ok(3));
let x = Cc::new(4);
let _y = x.clone();
assert_eq!(x.try_unwrap(), Err(Cc::new(4)));
let x = Cc::new(5);
let _w = x.downgrade();
assert_eq!(x.try_unwrap(), Err(Cc::new(5)));
}
collect_cycles();
}
#[test]
fn get_mut() {
{
let mut x = Cc::new(3);
*x.get_mut().unwrap() = 4;
assert_eq!(*x, 4);
let y = x.clone();
assert!(x.get_mut().is_none());
drop(y);
assert!(x.get_mut().is_some());
let _w = x.downgrade();
assert!(x.get_mut().is_none());
}
collect_cycles();
}
#[test]
fn test_cowrc_clone_make_unique() {
{
let mut cow0 = Cc::new(75);
let mut cow1 = cow0.clone();
let mut cow2 = cow1.clone();
assert!(75 == *cow0.make_unique());
assert!(75 == *cow1.make_unique());
assert!(75 == *cow2.make_unique());
*cow0.make_unique() += 1;
*cow1.make_unique() += 2;
*cow2.make_unique() += 3;
assert!(76 == *cow0);
assert!(77 == *cow1);
assert!(78 == *cow2);
assert!(*cow0 != *cow1);
assert!(*cow0 != *cow2);
assert!(*cow1 != *cow2);
}
collect_cycles();
}
#[test]
fn test_cowrc_clone_unique2() {
{
let mut cow0 = Cc::new(75);
let cow1 = cow0.clone();
let cow2 = cow1.clone();
assert!(75 == *cow0);
assert!(75 == *cow1);
assert!(75 == *cow2);
*cow0.make_unique() += 1;
assert!(76 == *cow0);
assert!(75 == *cow1);
assert!(75 == *cow2);
assert!(*cow0 != *cow1);
assert!(*cow0 != *cow2);
assert!(*cow1 == *cow2);
}
collect_cycles();
}
#[test]
fn test_cowrc_clone_weak() {
{
let mut cow0 = Cc::new(75);
let cow1_weak = cow0.downgrade();
assert!(75 == *cow0);
assert!(75 == *cow1_weak.upgrade().unwrap());
*cow0.make_unique() += 1;
assert!(76 == *cow0);
assert!(cow1_weak.upgrade().is_none());
}
collect_cycles();
}
#[test]
fn test_show() {
let foo = Cc::new(75);
assert_eq!(format!("{:?}", foo), "75");
}
#[cfg(not(all(target_os = "macos", miri)))]
#[test]
fn test_map() {
let mut map = std::collections::HashMap::new();
map.insert("Foo".to_string(), 4);
let x = Cc::new(map);
assert_eq!(x.get("Foo"), Some(&4));
}
#[test]
fn list_cycle() {
use std::cell::RefCell;
struct List(Vec<Cc<RefCell<List>>>);
impl Trace for List {
fn trace(&self, tracer: &mut Tracer) {
self.0.trace(tracer);
}
}
{
let a = Cc::new(RefCell::new(List(Vec::new())));
let b = Cc::new(RefCell::new(List(Vec::new())));
{
let mut a = a.borrow_mut();
a.0.push(b.clone());
}
{
let mut b = b.borrow_mut();
b.0.push(a.clone());
}
}
collect_cycles();
}
#[test]
fn test_retain_weak() {
let retained_weak_a;
{
struct A {
x: Cc<RefCell<Option<A>>>,
}
struct WeakA {
_x: Weak<RefCell<Option<A>>>
}
impl A {
fn downgrade(this: &Self) -> WeakA {
WeakA { _x: Cc::downgrade(&this.x) }
}
}
impl Clone for A {
fn clone(&self) -> Self {
A { x: self.x.clone() }
}
}
impl Trace for A {
fn trace(&self, tracer: &mut Tracer) {
self.x.trace(tracer);
}
}
let a = A { x: Cc::new(RefCell::new(None)) };
*a.x.borrow_mut() = Some(a.clone());
retained_weak_a = A::downgrade(&a);
}
collect_cycles();
let _x = retained_weak_a;
}
#[test]
fn test_double_visit_scan_black() {
let count = std::rc::Rc::new(std::cell::Cell::new(0));
struct A {
count: std::rc::Rc<std::cell::Cell<i32>>,
next_op: Cc<RefCell<Option<A>>>
}
impl Clone for A {
fn clone(&self) -> Self {
self.count.set(self.count.get() + 1);
A {
count: self.count.clone(),
next_op: self.next_op.clone()
}
}
}
impl Trace for A {
fn trace(&self, tracer: &mut Tracer) {
self.next_op.trace(tracer);
}
}
impl A {
fn new(count: std::rc::Rc<std::cell::Cell<i32>>, next_op: Option<A>) -> A {
count.set(count.get() + 1);
A {
count,
next_op: Cc::new(RefCell::new(next_op))
}
}
}
impl Drop for A {
fn drop(&mut self) {
self.count.set(self.count.get() - 1);
}
}
{
let q;
{
let z = A::new(count.clone(), None);
let y = A::new(count.clone(), Some(z.clone()));
let x = A::new(count.clone(), Some(y));
*z.next_op.borrow_mut() = Some(x.clone());
q = x;
}
collect_cycles();
*q.next_op.borrow_mut() = None;
}
collect_cycles();
assert_eq!(count.get(), 0);
}
}