wrc 2.1.0

A thread-safe weighted reference counting smart-pointer for Rust.
Documentation
use crate::Wrc;
use std::cell::Cell;

struct Canary<'a>(&'a Cell<usize>);

impl<'a> Drop for Canary<'a> {
    fn drop(&mut self) {
        let drop_count = self.0.get();
        self.0.set(drop_count + 1);
    }
}

#[test]
fn initializes_with_default_weight() {
    let wrc = Wrc::new(5);
    assert_eq!(wrc.local_weight(), 1 << 16);
    assert_eq!(Wrc::total_weight(&wrc), 1 << 16);
}

#[test]
fn clone_splits_weight() {
    let wrc0 = Wrc::new(5);
    assert_eq!(wrc0.local_weight(), 1 << 16);

    let wrc1 = wrc0.clone();
    assert_eq!(
        wrc0.local_weight() + wrc1.local_weight(),
        Wrc::total_weight(&wrc0)
    );
}

#[allow(clippy::redundant_clone)]
#[test]
fn more_weight_is_allocated_with_weight_exhausted() {
    let wrc0 = Wrc::new(5);
    let wrc1 = wrc0
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone()
        .clone();
    assert_eq!(wrc1.local_weight(), 1);

    // Intermediate weights are dropped because they go out of scope.
    assert_eq!(Wrc::total_weight(&wrc0), (1 << 15) + 1);

    let wrc2 = wrc1.clone();
    assert_eq!(wrc1.local_weight(), 1);
    assert_eq!(wrc2.local_weight(), 1 << 16);
    assert_eq!(Wrc::total_weight(&wrc0), (1 << 15) + 1 + (1 << 16));
}

#[test]
fn dropping_refs_drops_weight_from_inner() {
    let wrc0 = Wrc::new(5);
    let wrc1 = wrc0.clone();
    let wrc2 = wrc1.clone();
    let wrc3 = wrc2.clone();

    assert_eq!(wrc0.local_weight(), 1 << 15);
    assert_eq!(wrc1.local_weight(), 1 << 14);
    assert_eq!(wrc2.local_weight(), 1 << 13);
    assert_eq!(wrc3.local_weight(), 1 << 13);
    assert_eq!(Wrc::total_weight(&wrc0), 1 << 16);

    drop(wrc3);
    assert_eq!(Wrc::total_weight(&wrc0), (1 << 16) - (1 << 13));
    drop(wrc2);
    assert_eq!(Wrc::total_weight(&wrc0), (1 << 16) - (1 << 14));
    drop(wrc1);
    assert_eq!(Wrc::total_weight(&wrc0), 1 << 15);
}

#[test]
fn dropping_all_refs_drops_the_inner() {
    let drop_count = Cell::new(0);
    let wrc0 = Wrc::new(Canary(&drop_count));
    let wrc1 = wrc0.clone();
    assert_eq!(drop_count.get(), 0);
    drop(wrc0);
    assert_eq!(drop_count.get(), 0);
    drop(wrc1);
    assert_eq!(drop_count.get(), 1);
}

#[test]
fn get_mut_succeeds_when_unique() {
    let mut wrc = Wrc::new(5);
    {
        let mutref = Wrc::get_mut(&mut wrc).unwrap();
        *mutref = 13;
    }
    assert_eq!(*wrc, 13);
}

#[test]
fn get_mut_fails_when_not_unique() {
    let mut wrc = Wrc::new(5);
    let _wrc2 = wrc.clone();
    assert!(Wrc::get_mut(&mut wrc).is_none());
}

#[test]
fn get_mut_fails_when_weak_exists() {
    let mut wrc = Wrc::new(5);
    let _weak = Wrc::downgrade(&wrc);
    assert!(Wrc::get_mut(&mut wrc).is_none());
}

#[test]
fn weak_upgrade_succeeds_while_strong_exists() {
    let wrc = Wrc::new(5);
    let weak = Wrc::downgrade(&wrc);
    assert_eq!(*weak.upgrade().unwrap(), 5);
}

#[test]
fn weak_upgrade_fails_after_strong_dropped() {
    let wrc = Wrc::new(5);
    let weak = Wrc::downgrade(&wrc);
    drop(wrc);
    assert!(weak.upgrade().is_none());
}

#[test]
fn weak_does_not_prevent_data_drop() {
    let drop_count = Cell::new(0);
    let wrc = Wrc::new(Canary(&drop_count));
    let weak = Wrc::downgrade(&wrc);
    assert_eq!(drop_count.get(), 0);
    drop(wrc);
    assert_eq!(drop_count.get(), 1);
    assert!(weak.upgrade().is_none());
}

#[test]
fn weak_clone_works() {
    let wrc = Wrc::new(5);
    let weak1 = Wrc::downgrade(&wrc);
    let weak2 = weak1.clone();
    assert_eq!(*weak1.upgrade().unwrap(), 5);
    assert_eq!(*weak2.upgrade().unwrap(), 5);
}

#[test]
fn multiple_weak_refs_can_exist() {
    let drop_count = Cell::new(0);
    let wrc = Wrc::new(Canary(&drop_count));
    let weak1 = Wrc::downgrade(&wrc);
    let weak2 = Wrc::downgrade(&wrc);
    let weak3 = weak1.clone();

    drop(wrc);
    assert_eq!(drop_count.get(), 1);

    assert!(weak1.upgrade().is_none());
    assert!(weak2.upgrade().is_none());
    assert!(weak3.upgrade().is_none());
}