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
10unsafe 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 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
33unsafe 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 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
60unsafe 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
83unsafe 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
106unsafe 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
129unsafe fn cow_borrow<T: ?Sized + ToOwned + 'static>(this: PtrConst) -> PtrConst
134where
135 T::Owned: 'static,
136{
137 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
144unsafe 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 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 unsafe fn default_in_place<T: ?Sized + ToOwned + 'static>(dst: OxPtrMut)
209 where
210 T::Owned: Facet<'static> + 'static,
211 {
212 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 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 unsafe { alloc::alloc::dealloc(owned_ptr, owned_layout) };
236 return;
237 }
238
239 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 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 let cow_uninit_ptr = cow_shape.allocate().unwrap();
358
359 let new_into_fn = cow_def
361 .vtable
362 .new_into_fn
363 .expect("Cow<'_, T> should have new_into_fn");
364
365 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 let borrow_fn = cow_def
377 .vtable
378 .borrow_fn
379 .expect("Cow<'_, T> should have borrow_fn");
380
381 let borrowed_ptr = unsafe { borrow_fn(cow_ptr.as_const()) };
383 assert_eq!(unsafe { borrowed_ptr.get::<str>() }, "example");
385
386 unsafe {
389 cow_shape
390 .call_drop_in_place(cow_ptr)
391 .expect("Cow<'_, T> should have drop_in_place");
392 }
393
394 unsafe { cow_shape.deallocate_mut(cow_ptr).unwrap() };
397 }
398}