Skip to main content

facet_core/impls/alloc/
cow.rs

1use crate::{
2    Def, Facet, KnownPointer, OxPtrConst, OxPtrMut, PointerDef, PointerFlags, PointerVTable,
3    PtrConst, Shape, ShapeBuilder, Type, TypeNameFn, TypeNameOpts, TypeOpsIndirect, TypeParam,
4    UserType, VTableIndirect,
5};
6use crate::{PtrMut, PtrUninit};
7use alloc::borrow::Cow;
8use alloc::borrow::ToOwned;
9
10/// Debug for `Cow<T>` - delegates to inner T's debug
11///
12/// # Safety
13/// The pointer must point to a valid Cow<'_, T> value
14unsafe fn cow_debug<T: ?Sized + ToOwned + 'static>(
15    ox: OxPtrConst,
16    f: &mut core::fmt::Formatter<'_>,
17) -> Option<core::fmt::Result>
18where
19    T::Owned: 'static,
20{
21    let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
22
23    // Get T's shape from the Cow's shape
24    let cow_shape = ox.shape();
25    let t_shape = cow_shape.inner?;
26
27    let inner_ref: &T = cow_ref.as_ref();
28
29    let inner_ptr = PtrConst::new(inner_ref as *const T);
30    unsafe { t_shape.call_debug(inner_ptr, f) }
31}
32
33/// Display for `Cow<T>` - delegates to inner T's display if available
34///
35/// # Safety
36/// The pointer must point to a valid Cow<'_, T> value
37unsafe fn cow_display<T: ?Sized + ToOwned + 'static>(
38    ox: OxPtrConst,
39    f: &mut core::fmt::Formatter<'_>,
40) -> Option<core::fmt::Result>
41where
42    T::Owned: 'static,
43{
44    let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
45
46    // Get T's shape from the Cow's shape
47    let cow_shape = ox.shape();
48    let t_shape = cow_shape.inner?;
49
50    if !t_shape.vtable.has_display() {
51        return None;
52    }
53
54    let inner_ref: &T = cow_ref.as_ref();
55    let inner_ptr = PtrConst::new(inner_ref as *const T);
56
57    unsafe { t_shape.call_display(inner_ptr, f) }
58}
59
60/// PartialEq for `Cow<T>`
61///
62/// # Safety
63/// Both pointers must point to valid Cow<'_, T> values
64unsafe fn cow_partial_eq<T: ?Sized + ToOwned + 'static>(
65    a: OxPtrConst,
66    b: OxPtrConst,
67) -> Option<bool>
68where
69    T::Owned: 'static,
70{
71    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
72    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
73
74    let cow_shape = a.shape();
75    let t_shape = cow_shape.inner?;
76
77    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
78    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
79
80    unsafe { t_shape.call_partial_eq(a_inner, b_inner) }
81}
82
83/// PartialOrd for `Cow<T>`
84///
85/// # Safety
86/// Both pointers must point to valid Cow<'_, T> values
87unsafe fn cow_partial_cmp<T: ?Sized + ToOwned + 'static>(
88    a: OxPtrConst,
89    b: OxPtrConst,
90) -> Option<Option<core::cmp::Ordering>>
91where
92    T::Owned: 'static,
93{
94    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
95    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
96
97    let cow_shape = a.shape();
98    let t_shape = cow_shape.inner?;
99
100    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
101    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
102
103    unsafe { t_shape.call_partial_cmp(a_inner, b_inner) }
104}
105
106/// Ord for `Cow<T>`
107///
108/// # Safety
109/// Both pointers must point to valid Cow<'_, T> values
110unsafe fn cow_cmp<T: ?Sized + ToOwned + 'static>(
111    a: OxPtrConst,
112    b: OxPtrConst,
113) -> Option<core::cmp::Ordering>
114where
115    T::Owned: 'static,
116{
117    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
118    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
119
120    let cow_shape = a.shape();
121    let t_shape = cow_shape.inner?;
122
123    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
124    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
125
126    unsafe { t_shape.call_cmp(a_inner, b_inner) }
127}
128
129/// Borrow the inner value from `Cow<T>`
130///
131/// # Safety
132/// `this` must point to a valid Cow<'_, T> value
133unsafe fn cow_borrow<T: ?Sized + ToOwned + 'static>(this: PtrConst) -> PtrConst
134where
135    T::Owned: 'static,
136{
137    // SAFETY: Same layout reasoning as cow_debug
138    let cow_ref: &Cow<'_, T> =
139        unsafe { &*(this.as_byte_ptr() as *const alloc::borrow::Cow<'_, T>) };
140    let inner_ref: &T = cow_ref.as_ref();
141    PtrConst::new(inner_ref as *const T)
142}
143
144/// Create a new `Cow<T>` from a borrowed value
145unsafe fn cow_new_into<T: ?Sized + ToOwned + 'static>(this: PtrUninit, ptr: PtrMut) -> PtrMut
146where
147    T::Owned: 'static,
148{
149    unsafe { this.put(Cow::<'_, T>::Borrowed(ptr.read())) }
150}
151
152unsafe impl<'a, T> Facet<'a> for Cow<'a, T>
153where
154    T: 'a + ?Sized + ToOwned + 'static,
155    T: Facet<'a>,
156    T::Owned: Facet<'static>,
157{
158    const SHAPE: &'static Shape = &const {
159        const fn build_cow_vtable<T: ?Sized + ToOwned + 'static>() -> VTableIndirect
160        where
161            T::Owned: Facet<'static> + 'static,
162        {
163            VTableIndirect {
164                debug: Some(cow_debug::<T>),
165                display: Some(cow_display::<T>),
166                partial_eq: Some(cow_partial_eq::<T>),
167                partial_cmp: Some(cow_partial_cmp::<T>),
168                cmp: Some(cow_cmp::<T>),
169                ..VTableIndirect::EMPTY
170            }
171        }
172
173        const fn build_cow_type_ops<'facet, T>() -> TypeOpsIndirect
174        where
175            T: ?Sized + ToOwned + 'static + Facet<'facet>,
176            T::Owned: Facet<'static> + 'static,
177        {
178            unsafe fn drop_in_place<T: ?Sized + ToOwned + 'static>(ox: OxPtrMut)
179            where
180                T::Owned: 'static,
181            {
182                unsafe {
183                    core::ptr::drop_in_place(
184                        ox.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T>
185                    )
186                };
187            }
188
189            unsafe fn clone_into<T: ?Sized + ToOwned + 'static>(src: OxPtrConst, dst: OxPtrMut)
190            where
191                T::Owned: 'static,
192            {
193                let src_cow_ref: &Cow<'_, T> = unsafe { src.get::<Cow<'static, T>>() };
194                let cloned = src_cow_ref.clone();
195                // IMPORTANT: `clone_into` must be valid for writes to potentially-uninitialized
196                // destination memory. Do not create `&mut Cow` here (that would assume initialization
197                // and the assignment would drop garbage).
198                let out: *mut Cow<'static, T> =
199                    unsafe { dst.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T> };
200                unsafe { core::ptr::write(out, cloned) };
201            }
202
203            /// Default for `Cow<T>` - creates `Cow::Owned(T::Owned::default())`
204            /// by checking if T::Owned supports default at runtime via its shape.
205            ///
206            /// # Safety
207            /// dst must be valid for writes
208            unsafe fn default_in_place<T: ?Sized + ToOwned + 'static>(dst: OxPtrMut)
209            where
210                T::Owned: Facet<'static> + 'static,
211            {
212                // Get the Owned type's shape from the second type param
213                let cow_shape = dst.shape();
214                let type_params = cow_shape.type_params;
215                if type_params.len() < 2 {
216                    return;
217                }
218
219                let owned_shape = type_params[1].shape;
220
221                // Allocate space for T::Owned and call default_in_place
222                let owned_layout = match owned_shape.layout.sized_layout() {
223                    Ok(layout) => layout,
224                    Err(_) => return,
225                };
226
227                let owned_ptr = unsafe { alloc::alloc::alloc(owned_layout) };
228                if owned_ptr.is_null() {
229                    return;
230                }
231
232                let owned_uninit = crate::PtrMut::new(owned_ptr);
233                if unsafe { owned_shape.call_default_in_place(owned_uninit) }.is_none() {
234                    // Default not supported, deallocate and return
235                    unsafe { alloc::alloc::dealloc(owned_ptr, owned_layout) };
236                    return;
237                }
238
239                // Move the constructed T::Owned out of the temporary allocation.
240                // This leaves `owned_ptr` uninitialized, so we must deallocate the backing storage.
241                let owned_value: T::Owned =
242                    unsafe { core::ptr::read(owned_ptr as *const T::Owned) };
243                unsafe { alloc::alloc::dealloc(owned_ptr, owned_layout) };
244
245                // IMPORTANT: `default_in_place` must be valid for writes to potentially-uninitialized
246                // destination memory. Do not create `&mut Cow` here (that would assume initialization).
247                let out: *mut Cow<'static, T> =
248                    unsafe { dst.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T> };
249                unsafe { core::ptr::write(out, Cow::Owned(owned_value)) };
250            }
251
252            unsafe fn truthy<'facet, T>(ptr: PtrConst) -> bool
253            where
254                T: ?Sized + ToOwned + 'static + Facet<'facet>,
255                T::Owned: Facet<'static> + 'static,
256            {
257                let cow_ref: &Cow<'_, T> = unsafe { ptr.get::<Cow<'static, T>>() };
258                let inner_shape = <T as Facet<'facet>>::SHAPE;
259                if let Some(truthy) = inner_shape.truthiness_fn() {
260                    let inner: &T = cow_ref.as_ref();
261                    unsafe { truthy(PtrConst::new(inner as *const T)) }
262                } else {
263                    false
264                }
265            }
266
267            TypeOpsIndirect {
268                drop_in_place: drop_in_place::<T>,
269                default_in_place: Some(default_in_place::<T>),
270                clone_into: Some(clone_into::<T>),
271                is_truthy: Some(truthy::<'facet, T>),
272            }
273        }
274
275        const fn build_type_name<'a, T: Facet<'a> + ?Sized + ToOwned>() -> TypeNameFn {
276            fn type_name_impl<'a, T: Facet<'a> + ?Sized + ToOwned>(
277                _shape: &'static Shape,
278                f: &mut core::fmt::Formatter<'_>,
279                opts: TypeNameOpts,
280            ) -> core::fmt::Result {
281                write!(f, "Cow")?;
282                if let Some(opts) = opts.for_children() {
283                    write!(f, "<")?;
284                    T::SHAPE.write_type_name(f, opts)?;
285                    write!(f, ">")?;
286                } else {
287                    write!(f, "<…>")?;
288                }
289                Ok(())
290            }
291            type_name_impl::<T>
292        }
293
294        ShapeBuilder::for_sized::<Cow<'a, T>>("Cow")
295            .module_path("alloc::borrow")
296            .type_name(build_type_name::<T>())
297            .ty(Type::User(UserType::Opaque))
298            .def(Def::Pointer(PointerDef {
299                vtable: &const {
300                    PointerVTable {
301                        borrow_fn: Some(cow_borrow::<T>),
302                        new_into_fn: Some(cow_new_into::<T>),
303                        ..PointerVTable::new()
304                    }
305                },
306                pointee: Some(T::SHAPE),
307                weak: None,
308                strong: None,
309                flags: PointerFlags::EMPTY,
310                known: Some(KnownPointer::Cow),
311            }))
312            .type_params(&[
313                TypeParam {
314                    name: "T",
315                    shape: T::SHAPE,
316                },
317                TypeParam {
318                    name: "Owned",
319                    shape: <T::Owned>::SHAPE,
320                },
321            ])
322            .inner(T::SHAPE)
323            .vtable_indirect(&const { build_cow_vtable::<T>() })
324            .type_ops_indirect(&const { build_cow_type_ops::<'a, T>() })
325            .build()
326    };
327}
328
329#[cfg(test)]
330mod tests {
331    use core::{mem::ManuallyDrop, ptr::NonNull};
332
333    use alloc::string::String;
334
335    use super::*;
336
337    #[test]
338    fn test_cow_type_params() {
339        let [type_param_1, type_param_2] = <Cow<'_, str>>::SHAPE.type_params else {
340            panic!("Cow<'_, T> should only have 2 type params")
341        };
342        assert_eq!(type_param_1.shape(), str::SHAPE);
343        assert_eq!(type_param_2.shape(), String::SHAPE);
344    }
345
346    #[test]
347    fn test_cow_vtable_1_new_borrow_drop() {
348        facet_testhelpers::setup();
349
350        let cow_shape = <Cow<'_, str>>::SHAPE;
351        let cow_def = cow_shape
352            .def
353            .into_pointer()
354            .expect("Cow<'_, T> should have a smart pointer definition");
355
356        // Allocate memory for the Cow
357        let cow_uninit_ptr = cow_shape.allocate().unwrap();
358
359        // Get the function pointer for creating a new Cow from a value
360        let new_into_fn = cow_def
361            .vtable
362            .new_into_fn
363            .expect("Cow<'_, T> should have new_into_fn");
364
365        // Create the value and initialize the Cow
366        let mut value = ManuallyDrop::new("example");
367        let cow_ptr = unsafe {
368            new_into_fn(
369                cow_uninit_ptr,
370                PtrMut::new(NonNull::from(&mut value).as_ptr()),
371            )
372        };
373        // The value now belongs to the Cow, prevent its drop
374
375        // Get the function pointer for borrowing the inner value
376        let borrow_fn = cow_def
377            .vtable
378            .borrow_fn
379            .expect("Cow<'_, T> should have borrow_fn");
380
381        // Borrow the inner value and check it
382        let borrowed_ptr = unsafe { borrow_fn(cow_ptr.as_const()) };
383        // SAFETY: borrowed_ptr points to a valid String within the Cow
384        assert_eq!(unsafe { borrowed_ptr.get::<str>() }, "example");
385
386        // Drop the value in place
387        // SAFETY: value_ptr points to a valid String
388        unsafe {
389            cow_shape
390                .call_drop_in_place(cow_ptr)
391                .expect("Cow<'_, T> should have drop_in_place");
392        }
393
394        // Deallocate the memory
395        // SAFETY: cow_ptr was allocated by cow_shape and is now dropped (but memory is still valid)
396        unsafe { cow_shape.deallocate_mut(cow_ptr).unwrap() };
397    }
398}