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 alloc::borrow::Cow;
7use alloc::borrow::ToOwned;
8
9/// Debug for `Cow<T>` - delegates to inner T's debug
10///
11/// # Safety
12/// The pointer must point to a valid Cow<'_, T> value
13unsafe fn cow_debug<T: ?Sized + ToOwned + 'static>(
14    ox: OxPtrConst,
15    f: &mut core::fmt::Formatter<'_>,
16) -> Option<core::fmt::Result>
17where
18    T::Owned: 'static,
19{
20    let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
21
22    // Get T's shape from the Cow's shape
23    let cow_shape = ox.shape();
24    let t_shape = cow_shape.inner?;
25
26    let inner_ref: &T = cow_ref.as_ref();
27
28    let inner_ptr = PtrConst::new(inner_ref as *const T);
29    unsafe { t_shape.call_debug(inner_ptr, f) }
30}
31
32/// Display for `Cow<T>` - delegates to inner T's display if available
33///
34/// # Safety
35/// The pointer must point to a valid Cow<'_, T> value
36unsafe fn cow_display<T: ?Sized + ToOwned + 'static>(
37    ox: OxPtrConst,
38    f: &mut core::fmt::Formatter<'_>,
39) -> Option<core::fmt::Result>
40where
41    T::Owned: 'static,
42{
43    let cow_ref: &Cow<'_, T> = unsafe { ox.get::<Cow<'static, T>>() };
44
45    // Get T's shape from the Cow's shape
46    let cow_shape = ox.shape();
47    let t_shape = cow_shape.inner?;
48
49    if !t_shape.vtable.has_display() {
50        return None;
51    }
52
53    let inner_ref: &T = cow_ref.as_ref();
54    let inner_ptr = PtrConst::new(inner_ref as *const T);
55
56    unsafe { t_shape.call_display(inner_ptr, f) }
57}
58
59/// PartialEq for `Cow<T>`
60///
61/// # Safety
62/// Both pointers must point to valid Cow<'_, T> values
63unsafe fn cow_partial_eq<T: ?Sized + ToOwned + 'static>(
64    a: OxPtrConst,
65    b: OxPtrConst,
66) -> Option<bool>
67where
68    T::Owned: 'static,
69{
70    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
71    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
72
73    let cow_shape = a.shape();
74    let t_shape = cow_shape.inner?;
75
76    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
77    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
78
79    unsafe { t_shape.call_partial_eq(a_inner, b_inner) }
80}
81
82/// PartialOrd for `Cow<T>`
83///
84/// # Safety
85/// Both pointers must point to valid Cow<'_, T> values
86unsafe fn cow_partial_cmp<T: ?Sized + ToOwned + 'static>(
87    a: OxPtrConst,
88    b: OxPtrConst,
89) -> Option<Option<core::cmp::Ordering>>
90where
91    T::Owned: 'static,
92{
93    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
94    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
95
96    let cow_shape = a.shape();
97    let t_shape = cow_shape.inner?;
98
99    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
100    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
101
102    unsafe { t_shape.call_partial_cmp(a_inner, b_inner) }
103}
104
105/// Ord for `Cow<T>`
106///
107/// # Safety
108/// Both pointers must point to valid Cow<'_, T> values
109unsafe fn cow_cmp<T: ?Sized + ToOwned + 'static>(
110    a: OxPtrConst,
111    b: OxPtrConst,
112) -> Option<core::cmp::Ordering>
113where
114    T::Owned: 'static,
115{
116    let a_cow_ref: &Cow<'_, T> = unsafe { a.get::<Cow<'static, T>>() };
117    let b_cow_ref: &Cow<'_, T> = unsafe { b.get::<Cow<'static, T>>() };
118
119    let cow_shape = a.shape();
120    let t_shape = cow_shape.inner?;
121
122    let a_inner = PtrConst::new(a_cow_ref.as_ref() as *const T);
123    let b_inner = PtrConst::new(b_cow_ref.as_ref() as *const T);
124
125    unsafe { t_shape.call_cmp(a_inner, b_inner) }
126}
127
128/// Borrow the inner value from `Cow<T>`
129///
130/// # Safety
131/// `this` must point to a valid Cow<'_, T> value
132unsafe fn cow_borrow<T: ?Sized + ToOwned + 'static>(this: PtrConst) -> PtrConst
133where
134    T::Owned: 'static,
135{
136    // SAFETY: Same layout reasoning as cow_debug
137    let cow_ref: &Cow<'_, T> =
138        unsafe { &*(this.as_byte_ptr() as *const alloc::borrow::Cow<'_, T>) };
139    let inner_ref: &T = cow_ref.as_ref();
140    PtrConst::new(inner_ref as *const T)
141}
142
143unsafe impl<'a, T> Facet<'a> for Cow<'a, T>
144where
145    T: 'a + ?Sized + ToOwned + 'static,
146    T: Facet<'a>,
147    T::Owned: Facet<'static>,
148{
149    const SHAPE: &'static Shape = &const {
150        const fn build_cow_vtable<T: ?Sized + ToOwned + 'static>() -> VTableIndirect
151        where
152            T::Owned: Facet<'static> + 'static,
153        {
154            VTableIndirect {
155                debug: Some(cow_debug::<T>),
156                display: Some(cow_display::<T>),
157                partial_eq: Some(cow_partial_eq::<T>),
158                partial_cmp: Some(cow_partial_cmp::<T>),
159                cmp: Some(cow_cmp::<T>),
160                ..VTableIndirect::EMPTY
161            }
162        }
163
164        const fn build_cow_type_ops<'facet, T>() -> TypeOpsIndirect
165        where
166            T: ?Sized + ToOwned + 'static + Facet<'facet>,
167            T::Owned: Facet<'static> + 'static,
168        {
169            unsafe fn drop_in_place<T: ?Sized + ToOwned + 'static>(ox: OxPtrMut)
170            where
171                T::Owned: 'static,
172            {
173                unsafe {
174                    core::ptr::drop_in_place(
175                        ox.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T>
176                    )
177                };
178            }
179
180            unsafe fn clone_into<T: ?Sized + ToOwned + 'static>(src: OxPtrConst, dst: OxPtrMut)
181            where
182                T::Owned: 'static,
183            {
184                let src_cow_ref: &Cow<'_, T> = unsafe { src.get::<Cow<'static, T>>() };
185                let cloned = src_cow_ref.clone();
186                // IMPORTANT: `clone_into` must be valid for writes to potentially-uninitialized
187                // destination memory. Do not create `&mut Cow` here (that would assume initialization
188                // and the assignment would drop garbage).
189                let out: *mut Cow<'static, T> =
190                    unsafe { dst.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T> };
191                unsafe { core::ptr::write(out, cloned) };
192            }
193
194            /// Default for `Cow<T>` - creates `Cow::Owned(T::Owned::default())`
195            /// by checking if T::Owned supports default at runtime via its shape.
196            ///
197            /// # Safety
198            /// dst must be valid for writes
199            unsafe fn default_in_place<T: ?Sized + ToOwned + 'static>(dst: OxPtrMut)
200            where
201                T::Owned: Facet<'static> + 'static,
202            {
203                // Get the Owned type's shape from the second type param
204                let cow_shape = dst.shape();
205                let type_params = cow_shape.type_params;
206                if type_params.len() < 2 {
207                    return;
208                }
209
210                let owned_shape = type_params[1].shape;
211
212                // Allocate space for T::Owned and call default_in_place
213                let owned_layout = match owned_shape.layout.sized_layout() {
214                    Ok(layout) => layout,
215                    Err(_) => return,
216                };
217
218                let owned_ptr = unsafe { alloc::alloc::alloc(owned_layout) };
219                if owned_ptr.is_null() {
220                    return;
221                }
222
223                let owned_uninit = crate::PtrMut::new(owned_ptr);
224                if unsafe { owned_shape.call_default_in_place(owned_uninit) }.is_none() {
225                    // Default not supported, deallocate and return
226                    unsafe { alloc::alloc::dealloc(owned_ptr, owned_layout) };
227                    return;
228                }
229
230                // Move the constructed T::Owned out of the temporary allocation.
231                // This leaves `owned_ptr` uninitialized, so we must deallocate the backing storage.
232                let owned_value: T::Owned =
233                    unsafe { core::ptr::read(owned_ptr as *const T::Owned) };
234                unsafe { alloc::alloc::dealloc(owned_ptr, owned_layout) };
235
236                // IMPORTANT: `default_in_place` must be valid for writes to potentially-uninitialized
237                // destination memory. Do not create `&mut Cow` here (that would assume initialization).
238                let out: *mut Cow<'static, T> =
239                    unsafe { dst.ptr().as_ptr::<Cow<'static, T>>() as *mut Cow<'static, T> };
240                unsafe { core::ptr::write(out, Cow::Owned(owned_value)) };
241            }
242
243            unsafe fn truthy<'facet, T>(ptr: PtrConst) -> bool
244            where
245                T: ?Sized + ToOwned + 'static + Facet<'facet>,
246                T::Owned: Facet<'static> + 'static,
247            {
248                let cow_ref: &Cow<'_, T> = unsafe { ptr.get::<Cow<'static, T>>() };
249                let inner_shape = <T as Facet<'facet>>::SHAPE;
250                if let Some(truthy) = inner_shape.truthiness_fn() {
251                    let inner: &T = cow_ref.as_ref();
252                    unsafe { truthy(PtrConst::new(inner as *const T)) }
253                } else {
254                    false
255                }
256            }
257
258            TypeOpsIndirect {
259                drop_in_place: drop_in_place::<T>,
260                default_in_place: Some(default_in_place::<T>),
261                clone_into: Some(clone_into::<T>),
262                is_truthy: Some(truthy::<'facet, T>),
263            }
264        }
265
266        const fn build_type_name<'a, T: Facet<'a> + ?Sized + ToOwned>() -> TypeNameFn {
267            fn type_name_impl<'a, T: Facet<'a> + ?Sized + ToOwned>(
268                _shape: &'static Shape,
269                f: &mut core::fmt::Formatter<'_>,
270                opts: TypeNameOpts,
271            ) -> core::fmt::Result {
272                write!(f, "Cow")?;
273                if let Some(opts) = opts.for_children() {
274                    write!(f, "<")?;
275                    T::SHAPE.write_type_name(f, opts)?;
276                    write!(f, ">")?;
277                } else {
278                    write!(f, "<…>")?;
279                }
280                Ok(())
281            }
282            type_name_impl::<T>
283        }
284
285        ShapeBuilder::for_sized::<Cow<'a, T>>("Cow")
286            .type_name(build_type_name::<T>())
287            .ty(Type::User(UserType::Opaque))
288            .def(Def::Pointer(PointerDef {
289                vtable: &const {
290                    PointerVTable {
291                        borrow_fn: Some(cow_borrow::<T>),
292                        ..PointerVTable::new()
293                    }
294                },
295                pointee: Some(T::SHAPE),
296                weak: None,
297                strong: None,
298                flags: PointerFlags::EMPTY,
299                known: Some(KnownPointer::Cow),
300            }))
301            .type_params(&[
302                TypeParam {
303                    name: "T",
304                    shape: T::SHAPE,
305                },
306                TypeParam {
307                    name: "Owned",
308                    shape: <T::Owned>::SHAPE,
309                },
310            ])
311            .inner(T::SHAPE)
312            .vtable_indirect(&const { build_cow_vtable::<T>() })
313            .type_ops_indirect(&const { build_cow_type_ops::<'a, T>() })
314            .build()
315    };
316}