#![no_std]
use core::ptr::NonNull;
use core::{any::Any, ops::Deref};
use alloc::rc::Rc;
use alloc::sync::Arc;
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod std_impls;
pub struct Marc<T: ?Sized + 'static> {
data: NonNull<T>,
alloc: Arc<dyn Any + Send>,
}
unsafe impl<T: Sync + ?Sized> Send for Marc<T> {}
unsafe impl<T: Sync + ?Sized> Sync for Marc<T> {}
pub struct Mrc<T: ?Sized + 'static> {
data: NonNull<T>,
alloc: Rc<dyn Any>,
}
macro_rules! impls {
($name:ident, $l:literal) => {
impl<T: ?Sized> Deref for $name<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.data.as_ref() }
}
}
impl<T: ?Sized> $name<T> {
pub fn as_ptr(&self) -> *const T {
self.data.as_ptr() as *const _
}
#[doc = concat!("`", $l, "`")]
#[doc = concat!("`", $l, "`")]
#[doc = concat!("`", $l, "<T>`s")]
#[doc = concat!("`", $l, "<U>`s")]
pub fn map<U: ?Sized + 'static, F: FnOnce(&T) -> &U>(self_: Self, f: F) -> $name<U> {
let r = self_.deref();
let out = f(r) as *const _;
let data = unsafe { NonNull::new_unchecked(out as *mut _) };
$name {
data,
alloc: self_.alloc,
}
}
#[doc = concat!("`", $l, "<T>`")]
#[doc = concat!("[`", $l, "::map`]")]
pub fn try_map<U: ?Sized + 'static, F: FnOnce(&T) -> Option<&U>>(
self_: Self,
f: F,
) -> Result<$name<U>, $name<T>> {
let r = self_.deref();
match f(r) {
Some(p) => {
let data = unsafe { NonNull::new_unchecked(p as *const _ as *mut _) };
Ok($name {
data,
alloc: self_.alloc,
})
}
None => Err(self_),
}
}
#[doc = concat!("`", $l, "`")]
pub fn map_in_place<F: FnOnce(&T) -> &T>(self_: &mut Self, f: F) {
let r = self_.deref();
let out = f(r);
self_.data = unsafe { NonNull::new_unchecked(out as *const _ as *mut _) };
}
}
};
}
impls!(Marc, "Marc");
impls!(Mrc, "Mrc");
impl<T: Send + 'static> Marc<T> {
pub fn from_arc(arc: Arc<T>) -> Self {
let p = Arc::as_ptr(&arc) as *mut T;
unsafe {
Self {
data: NonNull::new_unchecked(p),
alloc: arc,
}
}
}
pub fn new(t: T) -> Self {
Marc::from_arc(Arc::new(t))
}
}
impl<T: ?Sized> Clone for Marc<T> {
fn clone(&self) -> Self {
Self {
data: self.data,
alloc: Arc::clone(&self.alloc),
}
}
}
impl<T: Send + 'static> From<Arc<T>> for Marc<T> {
fn from(a: Arc<T>) -> Self {
Marc::from_arc(a)
}
}
impl<T: 'static> Mrc<T> {
pub fn from_rc(rc: Rc<T>) -> Self {
let p = Rc::as_ptr(&rc) as *mut T;
unsafe {
Self {
data: NonNull::new_unchecked(p),
alloc: rc,
}
}
}
pub fn new(t: T) -> Self {
Mrc::from_rc(Rc::new(t))
}
}
impl<T: ?Sized> Clone for Mrc<T> {
fn clone(&self) -> Self {
Self {
data: self.data,
alloc: Rc::clone(&self.alloc),
}
}
}
impl<T: 'static> From<Rc<T>> for Mrc<T> {
fn from(a: Rc<T>) -> Self {
Mrc::from_rc(a)
}
}
#[cfg(test)]
mod tests {
use core::{cell::RefCell, marker::PhantomData, ops::DerefMut, panic::AssertUnwindSafe};
extern crate std;
use std::panic::catch_unwind;
use alloc::{string::ToString, vec};
use crate::*;
fn is_send<T: Send>(_: &T) {}
fn is_sync<T: Sync>(_: &T) {}
#[test]
fn clone_validity() {
let v = Mrc::new(vec![1, 2, 3]);
let c = v.clone();
let c = Mrc::map(c, |c| &c[1..]);
let v = v;
let c = c;
assert_eq!(&v[..], &[1, 2, 3]);
assert_eq!(&c[..], &[2, 3]);
}
#[test]
fn rc_validity() {
let a = Rc::new(vec![1, 2, 3]);
let m = Mrc::from_rc(Rc::clone(&a));
let m = Mrc::map(m, |m| &m[1..]);
let a = a;
let m = m;
assert_eq!(&a[..], &[1, 2, 3]);
assert_eq!(&m[..], &[2, 3]);
}
#[test]
fn init_correctness() {
let a = Rc::new(5);
assert_eq!(a.as_ref(), &5);
let b: Mrc<[i32]> = Mrc::from_borrow(vec![1, 2, 3, 4]);
assert_eq!(b.as_ref(), &[1, 2, 3, 4]);
let c: Mrc<i32> = Mrc::from_rc(Rc::new(5));
assert_eq!(c.as_ref(), &5);
}
#[test]
fn minimum_impls() {
let a = Marc::new(5);
is_send(&a);
is_sync(&a);
}
#[test]
fn non_sized_send_and_sync() {
let a: Marc<str> = "foobar".to_string().into();
is_send(&a);
is_sync(&a);
}
#[test]
fn mapped_sync() {
struct S(i32, PhantomData<RefCell<i32>>);
let m = Marc::new(S(10, PhantomData));
let m = Marc::map(m, |s| &s.0);
is_send(&m);
}
#[test]
fn in_place_panic() {
let mut m = Mrc::new(5);
let mut r = AssertUnwindSafe(&mut m);
catch_unwind(move || Mrc::map_in_place(r.deref_mut(), |_| panic!())).unwrap_err();
assert_eq!(m.as_ref(), &5);
}
}