use crate::{
Def, Facet, KnownPointer, OxPtrConst, OxPtrMut, OxPtrUninit, PointerDef, PointerFlags,
PointerVTable, PtrConst, Shape, ShapeBuilder, Type, TypeNameFn, TypeNameOpts, TypeOpsIndirect,
TypeParam, UserType, VTableIndirect,
};
use crate::{PtrMut, PtrUninit};
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
unsafe fn cow_debug<T: ?Sized + ToOwned + 'static>(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result>
where
T::Owned: 'static,
{
let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
let cow_shape = ox.shape();
let t_shape = cow_shape.inner?;
let inner_ref: &T = cow_ref.as_ref();
let inner_ptr = PtrConst::new(inner_ref as *const T);
unsafe { t_shape.call_debug(inner_ptr, f) }
}
unsafe fn cow_display<T: ?Sized + ToOwned + 'static>(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result>
where
T::Owned: 'static,
{
let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
let cow_shape = ox.shape();
let t_shape = cow_shape.inner?;
if !t_shape.vtable.has_display() {
return None;
}
let inner_ref: &T = cow_ref.as_ref();
let inner_ptr = PtrConst::new(inner_ref as *const T);
unsafe { t_shape.call_display(inner_ptr, f) }
}
unsafe fn cow_partial_eq<T: ?Sized + ToOwned + 'static>(
a: OxPtrConst,
b: OxPtrConst,
) -> Option<bool>
where
T::Owned: 'static,
{
let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
let cow_shape = a.shape();
let t_shape = cow_shape.inner?;
let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
unsafe { t_shape.call_partial_eq(a_inner, b_inner) }
}
unsafe fn cow_partial_cmp<T: ?Sized + ToOwned + 'static>(
a: OxPtrConst,
b: OxPtrConst,
) -> Option<Option<core::cmp::Ordering>>
where
T::Owned: 'static,
{
let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
let cow_shape = a.shape();
let t_shape = cow_shape.inner?;
let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
unsafe { t_shape.call_partial_cmp(a_inner, b_inner) }
}
unsafe fn cow_cmp<T: ?Sized + ToOwned + 'static>(
a: OxPtrConst,
b: OxPtrConst,
) -> Option<core::cmp::Ordering>
where
T::Owned: 'static,
{
let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
let cow_shape = a.shape();
let t_shape = cow_shape.inner?;
let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
unsafe { t_shape.call_cmp(a_inner, b_inner) }
}
unsafe extern "C" fn cow_borrow<T: ?Sized + ToOwned + 'static>(this: PtrConst) -> PtrConst
where
T::Owned: 'static,
{
let cow_ref: &Cow<'_, T> =
unsafe { &*(this.as_byte_ptr() as *const alloc::borrow::Cow<'_, T>) };
let inner_ref: &T = cow_ref.as_ref();
PtrConst::new(inner_ref as *const T)
}
unsafe extern "C" fn cow_new_into<T: ?Sized + ToOwned + 'static>(
this: PtrUninit,
ptr: PtrMut,
) -> PtrMut
where
T::Owned: 'static,
{
unsafe { this.put(Cow::<'_, T>::Borrowed(ptr.read())) }
}
unsafe impl<'a, T> Facet<'a> for Cow<'a, T>
where
T: 'a + ?Sized + ToOwned + 'static,
T: Facet<'a>,
T::Owned: Facet<'static>,
{
const SHAPE: &'static Shape = &const {
const fn build_cow_vtable<T: ?Sized + ToOwned + 'static>() -> VTableIndirect
where
T::Owned: Facet<'static> + 'static,
{
VTableIndirect {
debug: Some(cow_debug::<T>),
display: Some(cow_display::<T>),
partial_eq: Some(cow_partial_eq::<T>),
partial_cmp: Some(cow_partial_cmp::<T>),
cmp: Some(cow_cmp::<T>),
..VTableIndirect::EMPTY
}
}
const fn build_cow_type_ops<'facet, T>() -> TypeOpsIndirect
where
T: ?Sized + ToOwned + 'static + Facet<'facet>,
T::Owned: Facet<'static> + 'static,
{
unsafe fn drop_in_place<T: ?Sized + ToOwned + 'static>(ox: OxPtrMut)
where
T::Owned: 'static,
{
unsafe {
core::ptr::drop_in_place(
ox.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T>
)
};
}
unsafe fn clone_into<T: ?Sized + ToOwned + 'static>(src: OxPtrConst, dst: OxPtrMut)
where
T::Owned: 'static,
{
let src_cow_ref: &Cow<'_, T> = unsafe { src.get::<Cow<'static, T>>() };
let cloned = src_cow_ref.clone();
let out: *mut Cow<'static, T> =
unsafe { dst.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T> };
unsafe { core::ptr::write(out, cloned) };
}
unsafe fn default_in_place<T: ?Sized + ToOwned + 'static>(dst: OxPtrUninit) -> bool
where
T::Owned: Facet<'static> + 'static,
{
let cow_shape = dst.shape();
let type_params = cow_shape.type_params;
if type_params.len() < 2 {
return false;
}
let owned_shape = type_params[1].shape;
let owned_layout = match owned_shape.layout.sized_layout() {
Ok(layout) => layout,
Err(_) => return false,
};
let owned_uninit = crate::alloc_for_layout(owned_layout);
if unsafe { owned_shape.call_default_in_place(owned_uninit) }.is_none() {
unsafe { crate::dealloc_for_layout(owned_uninit.assume_init(), owned_layout) };
return false;
}
let owned_value: T::Owned =
unsafe { core::ptr::read(owned_uninit.as_byte_ptr() as *const T::Owned) };
unsafe { crate::dealloc_for_layout(owned_uninit.assume_init(), owned_layout) };
unsafe { dst.put(Cow::<'static, T>::Owned(owned_value)) };
true
}
unsafe fn truthy<'facet, T>(ptr: PtrConst) -> bool
where
T: ?Sized + ToOwned + 'static + Facet<'facet>,
T::Owned: Facet<'static> + 'static,
{
let cow_ref: &Cow<'_, T> = unsafe { ptr.get::<Cow<'static, T>>() };
let inner_shape = <T as Facet<'facet>>::SHAPE;
if let Some(truthy) = inner_shape.truthiness_fn() {
let inner: &T = cow_ref.as_ref();
unsafe { truthy(PtrConst::new(inner as *const T)) }
} else {
false
}
}
TypeOpsIndirect {
drop_in_place: drop_in_place::<T>,
default_in_place: Some(default_in_place::<T>),
clone_into: Some(clone_into::<T>),
is_truthy: Some(truthy::<'facet, T>),
}
}
const fn build_type_name<'a, T: Facet<'a> + ?Sized + ToOwned>() -> TypeNameFn {
fn type_name_impl<'a, T: Facet<'a> + ?Sized + ToOwned>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "Cow")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<…>")?;
}
Ok(())
}
type_name_impl::<T>
}
ShapeBuilder::for_sized::<Cow<'a, T>>("Cow")
.module_path("alloc::borrow")
.type_name(build_type_name::<T>())
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
borrow_fn: Some(cow_borrow::<T>),
new_into_fn: Some(cow_new_into::<T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::EMPTY,
known: Some(KnownPointer::Cow),
}))
.type_params(&[
TypeParam {
name: "T",
shape: T::SHAPE,
},
TypeParam {
name: "Owned",
shape: <T::Owned>::SHAPE,
},
])
.inner(T::SHAPE)
.vtable_indirect(&const { build_cow_vtable::<T>() })
.type_ops_indirect(&const { build_cow_type_ops::<'a, T>() })
.build()
};
}
#[cfg(test)]
mod tests {
use core::{mem::ManuallyDrop, ptr::NonNull};
use alloc::string::String;
use super::*;
#[test]
fn test_cow_type_params() {
let [type_param_1, type_param_2] = <Cow<'_, str>>::SHAPE.type_params else {
panic!("Cow<'_, T> should only have 2 type params")
};
assert_eq!(type_param_1.shape(), str::SHAPE);
assert_eq!(type_param_2.shape(), String::SHAPE);
}
#[test]
fn test_cow_vtable_1_new_borrow_drop() {
facet_testhelpers::setup();
let cow_shape = <Cow<'_, str>>::SHAPE;
let cow_def = cow_shape
.def
.into_pointer()
.expect("Cow<'_, T> should have a smart pointer definition");
let cow_uninit_ptr = cow_shape.allocate().unwrap();
let new_into_fn = cow_def
.vtable
.new_into_fn
.expect("Cow<'_, T> should have new_into_fn");
let mut value = ManuallyDrop::new("example");
let cow_ptr = unsafe {
new_into_fn(
cow_uninit_ptr,
PtrMut::new(NonNull::from(&mut value).as_ptr()),
)
};
let borrow_fn = cow_def
.vtable
.borrow_fn
.expect("Cow<'_, T> should have borrow_fn");
let borrowed_ptr = unsafe { borrow_fn(cow_ptr.as_const()) };
assert_eq!(unsafe { borrowed_ptr.get::<str>() }, "example");
unsafe {
cow_shape
.call_drop_in_place(cow_ptr)
.expect("Cow<'_, T> should have drop_in_place");
}
unsafe { cow_shape.deallocate_mut(cow_ptr).unwrap() };
}
}