#![feature(ptr_alignment_type)]
use std::{
alloc::Layout,
cell::Cell,
mem::{MaybeUninit, forget, transmute},
ptr::Alignment,
rc::{Rc, Weak},
sync::atomic::{AtomicBool, Ordering},
};
static SANITY_CHECKED: AtomicBool = AtomicBool::new(false);
pub unsafe fn check_sanity() {
{
let uninit = RcUninit::new_assume_sane();
let weak = uninit.weak();
assert_eq!(0, Weak::strong_count(&weak));
assert_eq!(0, Weak::weak_count(&weak));
let inner_ptr = uninit.ptr();
let inner = unsafe { get_inner(inner_ptr) };
assert_eq!(inner.strong.get(), 0);
assert_eq!(inner.weak.get(), 3);
let weak2 = weak.clone();
assert_eq!(inner.strong.get(), 0);
assert_eq!(inner.weak.get(), 4);
assert_eq!(0, Weak::strong_count(&weak));
assert_eq!(0, Weak::weak_count(&weak));
assert!(weak.upgrade().is_none());
assert!(weak2.upgrade().is_none());
let _ = inner;
let rc = uninit.init(123);
let inner = unsafe { get_inner(inner_ptr) };
assert_eq!(inner.strong.get(), 1);
assert_eq!(inner.weak.get(), 3);
let rc2 = rc.clone();
assert_eq!(inner.strong.get(), 2);
assert_eq!(inner.weak.get(), 3);
assert_eq!(2, Weak::strong_count(&weak));
assert_eq!(2, Weak::weak_count(&weak));
assert!(Rc::ptr_eq(&rc, &rc2));
assert!(Rc::ptr_eq(&rc, &weak.upgrade().unwrap()));
assert!(Rc::ptr_eq(&rc, &weak2.upgrade().unwrap()));
drop(rc2);
assert_eq!(inner.strong.get(), 1);
assert_eq!(inner.weak.get(), 3);
assert_eq!(1, Weak::strong_count(&weak));
assert_eq!(2, Weak::weak_count(&weak));
drop(weak2);
assert_eq!(inner.strong.get(), 1);
assert_eq!(inner.weak.get(), 2);
assert_eq!(1, Weak::strong_count(&weak));
assert_eq!(1, Weak::weak_count(&weak));
drop(rc);
assert_eq!(inner.strong.get(), 0);
assert_eq!(inner.weak.get(), 1);
assert_eq!(0, Weak::strong_count(&weak));
assert_eq!(0, Weak::weak_count(&weak));
}
SANITY_CHECKED.store(true, Ordering::Relaxed);
}
#[repr(C)]
struct RcInner<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
pub struct RcUninit<T> {
ptr: *const MaybeUninit<T>,
weak: Weak<T>,
}
impl<T> RcUninit<T> {
pub fn new() -> Self {
assert!(
SANITY_CHECKED.load(Ordering::Relaxed),
"must call check_sanity() before using this library"
);
Self::new_assume_sane()
}
fn ptr(&self) -> *const MaybeUninit<T> {
self.ptr
}
fn new_assume_sane() -> Self {
let rc: Rc<MaybeUninit<T>> = Rc::new(MaybeUninit::uninit());
let weak: Weak<T> = unsafe { transmute(Rc::downgrade(&rc)) };
let ptr = Rc::into_raw(rc);
let inner: &RcInner<MaybeUninit<T>> = unsafe { get_inner(ptr) };
assert_eq!(inner.strong.get(), 1);
assert_eq!(inner.weak.get(), 2);
inner.strong.set(0);
Self { ptr, weak }
}
pub fn weak(&self) -> Weak<T> {
self.weak.clone()
}
pub fn init(self, value: T) -> Rc<T> {
let ptr = self.ptr;
let inner: &mut RcInner<MaybeUninit<T>> = unsafe { get_inner_mut(ptr) };
assert_eq!(inner.strong.get(), 0);
assert!(inner.weak.get() >= 2);
inner.strong.set(1);
inner.weak.set(inner.weak.get() - 1);
inner.value.write(value);
let rc = unsafe { Rc::from_raw(ptr) };
forget(self);
unsafe { rc.assume_init() }
}
pub fn init_with<F>(self, constructor: F) -> Rc<T>
where
F: FnOnce(Weak<T>) -> T,
{
let weak = self.weak();
self.init(constructor(weak))
}
}
impl<T> Default for RcUninit<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for RcUninit<T> {
fn drop(&mut self) {
let offset = data_offset::<T>();
let inner = unsafe { &mut *(self.ptr.byte_sub(offset) as *mut RcInner<MaybeUninit<T>>) };
inner.strong.set(1);
unsafe { Rc::from_raw(self.ptr) };
}
}
fn data_offset<T>() -> usize {
let layout = Layout::new::<RcInner<()>>();
layout.size() + layout.padding_needed_for(Alignment::of::<T>())
}
unsafe fn get_inner<'a, T>(ptr: *const MaybeUninit<T>) -> &'a RcInner<MaybeUninit<T>> {
let offset = data_offset::<T>();
unsafe { &*(ptr.byte_sub(offset) as *const RcInner<MaybeUninit<T>>) }
}
unsafe fn get_inner_mut<'a, T>(ptr: *const MaybeUninit<T>) -> &'a mut RcInner<MaybeUninit<T>> {
let offset = data_offset::<T>();
unsafe { &mut *(ptr.byte_sub(offset) as *mut RcInner<MaybeUninit<T>>) }
}
#[test]
fn basic() {
unsafe { check_sanity() };
let x = RcUninit::new();
let weak = x.weak();
assert!(weak.upgrade().is_none());
let rc = x.init(123);
assert_eq!(weak.upgrade().map(|x| *x), Some(123));
drop(rc);
assert!(weak.upgrade().is_none());
}
#[test]
fn dst() {
unsafe { check_sanity() };
let x = RcUninit::<i32>::new();
let weak = x.weak();
let weak: Weak<dyn std::fmt::Debug> = weak;
assert!(weak.upgrade().is_none());
let _rc = x.init(123);
let upgraded = weak.upgrade();
assert!(upgraded.is_some());
assert_eq!("123", format!("{:?}", upgraded.unwrap()));
}
#[test]
fn zst() {
unsafe { check_sanity() };
let x = RcUninit::<()>::new();
let weak = x.weak();
assert!(weak.upgrade().is_none());
let _rc = x.init(());
let upgraded = weak.upgrade();
assert!(upgraded.is_some());
}
#[test]
fn odd_sized_type() {
unsafe { check_sanity() };
let x = RcUninit::<[u8; 33]>::new();
let weak = x.weak();
assert!(weak.upgrade().is_none());
let rc = x.init([137u8; 33]);
let upgraded = weak.upgrade();
assert!(upgraded.is_some());
assert_eq!(33, rc.len());
for value in *rc {
assert_eq!(137u8, value);
}
}
#[test]
fn panic_on_drop_not_initialized() {
unsafe { check_sanity() };
struct PanicOnDrop;
impl Drop for PanicOnDrop {
fn drop(&mut self) {
panic!();
}
}
RcUninit::<PanicOnDrop>::new();
}
#[test]
fn count_drops() {
unsafe { check_sanity() };
let counter = Rc::new(Cell::new(0));
struct DropCounter(Rc<Cell<usize>>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
let uninit = RcUninit::<DropCounter>::new();
assert_eq!(counter.get(), 0);
uninit.weak();
assert_eq!(counter.get(), 0);
uninit.init(DropCounter(counter.clone()));
assert_eq!(counter.get(), 1);
let _ = RcUninit::<DropCounter>::new();
assert_eq!(counter.get(), 1);
}