use core::{fmt, marker::PhantomData, ptr};
use boc_sys as ffi;
use crate::descriptor::get_descriptor;
pub struct Cown<T> {
pub(crate) cown_ptr: ffi::CownPtr,
pub(crate) _marker: PhantomData<std::sync::Mutex<T>>,
}
#[repr(C)]
pub(crate) struct CownData<T> {
cown: ffi::OpaqueCown,
data: T,
}
pub(crate) fn cown_to_data<T>(ptr: ffi::CownPtr) -> *mut T {
let ptr = ptr.addr();
debug_assert!(!ptr.is_null());
debug_assert!((ptr as usize) & 15 == 0, "{ptr:p} not 16 bit aligned");
let p = ptr as *mut CownData<T>;
unsafe { ptr::addr_of_mut!((*p).data) }
}
impl<T> Cown<T> {
fn data_ptr(&self) -> *mut T {
cown_to_data(self.cown_ptr)
}
#[cfg(test)]
unsafe fn yolo_data(&mut self) -> &mut T {
&mut *(self.data_ptr() as *mut T)
}
}
impl<T> fmt::Pointer for Cown<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.cown_ptr.addr(), f)
}
}
impl<T> core::ops::Drop for Cown<T> {
fn drop(&mut self) {
unsafe { ffi::boxcars_release_object(self.cown_ptr) };
}
}
impl<T> Clone for Cown<T> {
fn clone(&self) -> Self {
unsafe {
ffi::boxcars_acquire_object(self.cown_ptr);
}
Self {
cown_ptr: self.cown_ptr,
_marker: PhantomData,
}
}
}
impl<T> Cown<T> {
pub fn new(value: T) -> Self {
unsafe {
let desc = get_descriptor::<CownData<T>>();
let cown_ptr = ffi::boxcars_allocate_cown(desc);
let this = Self {
cown_ptr,
_marker: PhantomData,
};
ptr::write(this.data_ptr(), value);
this
}
}
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use super::*;
use crate::scheduler::{self, with_leak_detector, with_scheduler};
#[test]
fn new() {
with_leak_detector(|| {
let v = Cown::new(10);
let v2 = v.clone();
assert_eq!(v.cown_ptr.addr(), v2.cown_ptr.addr());
drop(v);
drop(v2);
})
}
#[test]
fn new_minimal() {
with_scheduler(|| {
Cown::new(10);
})
}
#[test]
fn clone_minimal() {
with_scheduler(|| {
let v1 = Cown::new(42);
_ = v1.clone();
})
}
#[test]
fn clone_notnull() {
with_scheduler(|| {
let v1 = Cown::new(10);
let v2 = v1.clone();
assert_ne!(v2.cown_ptr.addr(), ptr::null_mut());
})
}
#[test]
fn leak_detector_new() {
unsafe {
ffi::enable_logging();
}
with_leak_detector(|| {
let x = Cown::new(1010);
let y = x.clone();
drop(x);
drop(y);
});
}
#[test]
fn read_modify_write() {
scheduler::with_leak_detector(|| {
let mut c = Cown::new([0; 100]);
assert_ne!(c.cown_ptr.addr(), ptr::null_mut());
{
let c = unsafe { c.yolo_data() };
for (n, el) in c.iter_mut().enumerate() {
assert_eq!(*el, 0);
*el = n;
}
}
let mut c1 = c.clone();
assert_ne!(c1.cown_ptr.addr(), ptr::null_mut());
{
for (n, el) in unsafe { c1.yolo_data() }.iter_mut().enumerate() {
assert_eq!(*el, n);
*el *= 2;
}
}
let mut c2 = c.clone();
assert_ne!(c2.cown_ptr.addr(), ptr::null_mut());
{
for (n, el) in unsafe { c2.yolo_data() }.iter().enumerate() {
assert_eq!(*el, n * 2);
}
}
})
}
#[test]
fn write_stress() {
fn stress_once<const N: usize>() {
let mut x = Cown::new([0u8; N]);
unsafe {
for i in x.yolo_data() {
assert_eq!(*i, 0);
*i = 0xCC;
}
}
let mut x2 = x.clone();
unsafe {
for i in x2.yolo_data() {
assert_eq!(*i, 0xCC);
*i = 0x33;
}
}
drop(x2);
unsafe {
for i in x.yolo_data() {
assert_eq!(*i, 0x33);
}
}
drop(x);
}
fn repeat_alloc<const N: usize>() {
for _ in 0..100 {
stress_once::<N>();
}
}
scheduler::with_leak_detector(|| {
repeat_alloc::<3932>();
repeat_alloc::<3719>();
repeat_alloc::<1477>();
repeat_alloc::<414>();
repeat_alloc::<163>();
repeat_alloc::<4>();
repeat_alloc::<3>();
repeat_alloc::<2>();
repeat_alloc::<1>();
repeat_alloc::<0>();
});
}
struct WriteOnDrop<'a>(&'a Cell<bool>);
impl Drop for WriteOnDrop<'_> {
fn drop(&mut self) {
assert_eq!(self.0.get(), false);
self.0.set(true);
}
}
#[test]
fn dtor() {
scheduler::with_scheduler(|| {
let flag = Cell::new(false);
let cown = Cown::new(WriteOnDrop(&flag));
assert_eq!(flag.get(), false);
drop(cown);
assert_eq!(flag.get(), true);
})
}
#[test]
fn dtor_clone() {
scheduler::with_scheduler(|| {
let flag = Cell::new(false);
let cown = Cown::new(WriteOnDrop(&flag));
assert_eq!(flag.get(), false);
let cown2 = cown.clone();
assert_eq!(flag.get(), false);
drop(cown);
assert_eq!(flag.get(), false);
drop(cown2);
assert_eq!(flag.get(), true);
})
}
}