facet_core/impls/core/
slice.rs

1use core::cmp::Ordering;
2
3use crate::{OxPtrConst, *};
4
5unsafe fn slice_len<T>(ptr: PtrConst) -> usize {
6    unsafe {
7        let slice = ptr.get::<[T]>();
8        slice.len()
9    }
10}
11
12unsafe fn slice_as_ptr<T>(ptr: PtrConst) -> PtrConst {
13    unsafe {
14        let slice = ptr.get::<[T]>();
15        PtrConst::new(slice.as_ptr())
16    }
17}
18
19unsafe fn slice_as_mut_ptr<T>(ptr: PtrMut) -> PtrMut {
20    unsafe {
21        let slice = ptr.as_mut::<[T]>();
22        PtrMut::new(slice.as_mut_ptr())
23    }
24}
25
26unsafe fn slice_drop(ox: OxPtrMut) {
27    let shape = ox.shape();
28    let Some(def) = get_slice_def(shape) else {
29        return;
30    };
31    let len = unsafe { (def.vtable.len)(ox.ptr().as_const()) };
32    let slice_ptr = unsafe { (def.vtable.as_mut_ptr)(ox.ptr()) };
33    let Some(stride) = def
34        .t
35        .layout
36        .sized_layout()
37        .ok()
38        .map(|l| l.pad_to_align().size())
39    else {
40        return;
41    };
42
43    for i in 0..len {
44        let elem_ptr = unsafe { PtrMut::new((slice_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
45        unsafe { def.t.call_drop_in_place(elem_ptr) };
46    }
47}
48
49#[inline(always)]
50unsafe fn slice_truthy<T>(ptr: PtrConst) -> bool {
51    !unsafe { ptr.get::<[T]>() }.is_empty()
52}
53
54/// Extract the SliceDef from a shape, returns None if not a slice
55#[inline]
56fn get_slice_def(shape: &'static Shape) -> Option<&'static SliceDef> {
57    match shape.def {
58        Def::Slice(ref def) => Some(def),
59        _ => None,
60    }
61}
62
63/// Debug for `[T]`
64unsafe fn slice_debug(
65    ox: OxPtrConst,
66    f: &mut core::fmt::Formatter<'_>,
67) -> Option<core::fmt::Result> {
68    let shape = ox.shape();
69    let def = get_slice_def(shape)?;
70    let ptr = ox.ptr();
71
72    let len = unsafe { (def.vtable.len)(ptr) };
73    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
74    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
75
76    let mut list = f.debug_list();
77    for i in 0..len {
78        // SAFETY: We're iterating within bounds of the slice, and the caller
79        // guarantees the OxPtrConst points to a valid slice.
80        let elem_ptr = unsafe { PtrConst::new((slice_ptr.raw_ptr()).add(i * stride)) };
81        let elem_ox = unsafe { OxRef::new(elem_ptr, def.t) };
82        list.entry(&elem_ox);
83    }
84    Some(list.finish())
85}
86
87/// Hash for `[T]`
88unsafe fn slice_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
89    let shape = ox.shape();
90    let def = get_slice_def(shape)?;
91    let ptr = ox.ptr();
92
93    let len = unsafe { (def.vtable.len)(ptr) };
94    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
95    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
96
97    for i in 0..len {
98        let elem_ptr = unsafe { PtrConst::new((slice_ptr.raw_ptr()).add(i * stride)) };
99        unsafe { def.t.call_hash(elem_ptr, hasher)? };
100    }
101    Some(())
102}
103
104/// PartialEq for `[T]`
105unsafe fn slice_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
106    let shape = a.shape();
107    let def = get_slice_def(shape)?;
108
109    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
110    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
111
112    if a_len != b_len {
113        return Some(false);
114    }
115
116    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
117    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
118    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
119
120    for i in 0..a_len {
121        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
122        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
123        if !unsafe { def.t.call_partial_eq(a_elem, b_elem)? } {
124            return Some(false);
125        }
126    }
127    Some(true)
128}
129
130/// PartialOrd for `[T]`
131unsafe fn slice_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
132    let shape = a.shape();
133    let def = get_slice_def(shape)?;
134
135    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
136    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
137
138    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
139    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
140    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
141
142    let min_len = a_len.min(b_len);
143    for i in 0..min_len {
144        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
145        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
146        match unsafe { def.t.call_partial_cmp(a_elem, b_elem)? } {
147            Some(Ordering::Equal) => continue,
148            other => return Some(other),
149        }
150    }
151    Some(Some(a_len.cmp(&b_len)))
152}
153
154/// Ord for `[T]`
155unsafe fn slice_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
156    let shape = a.shape();
157    let def = get_slice_def(shape)?;
158
159    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
160    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
161
162    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
163    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
164    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
165
166    let min_len = a_len.min(b_len);
167    for i in 0..min_len {
168        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
169        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
170        match unsafe { def.t.call_cmp(a_elem, b_elem)? } {
171            Ordering::Equal => continue,
172            other => return Some(other),
173        }
174    }
175    Some(a_len.cmp(&b_len))
176}
177
178// Shared vtable for all [T]
179const SLICE_VTABLE: VTableIndirect = VTableIndirect {
180    display: None,
181    debug: Some(slice_debug),
182    hash: Some(slice_hash),
183    invariants: None,
184    parse: None,
185    parse_bytes: None,
186    try_from: None,
187    try_into_inner: None,
188    try_borrow_inner: None,
189    partial_eq: Some(slice_partial_eq),
190    partial_cmp: Some(slice_partial_cmp),
191    cmp: Some(slice_cmp),
192};
193
194unsafe impl<'a, T> Facet<'a> for [T]
195where
196    T: Facet<'a>,
197{
198    const SHAPE: &'static Shape = &const {
199        const fn build_type_name<'a, T: Facet<'a>>() -> TypeNameFn {
200            fn type_name_impl<'a, T: Facet<'a>>(
201                _shape: &'static Shape,
202                f: &mut core::fmt::Formatter<'_>,
203                opts: TypeNameOpts,
204            ) -> core::fmt::Result {
205                if let Some(opts) = opts.for_children() {
206                    write!(f, "[")?;
207                    T::SHAPE.write_type_name(f, opts)?;
208                    write!(f, "]")
209                } else {
210                    write!(f, "[…]")
211                }
212            }
213            type_name_impl::<T>
214        }
215
216        const fn build_slice_vtable<'a, T: Facet<'a>>() -> SliceVTable {
217            SliceVTable {
218                len: slice_len::<T>,
219                as_ptr: slice_as_ptr::<T>,
220                as_mut_ptr: slice_as_mut_ptr::<T>,
221            }
222        }
223
224        ShapeBuilder::for_unsized::<Self>("[_]")
225            .type_name(build_type_name::<T>())
226            .vtable_indirect(&SLICE_VTABLE)
227            .ty(Type::Sequence(SequenceType::Slice(SliceType {
228                t: T::SHAPE,
229            })))
230            .def(Def::Slice(SliceDef::new(
231                &const { build_slice_vtable::<T>() },
232                T::SHAPE,
233            )))
234            .type_params(&[TypeParam {
235                name: "T",
236                shape: T::SHAPE,
237            }])
238            .type_ops_indirect(
239                &const {
240                    TypeOpsIndirect {
241                        drop_in_place: slice_drop,
242                        default_in_place: None,
243                        clone_into: None,
244                        is_truthy: Some(slice_truthy::<T>),
245                    }
246                },
247            )
248            .build()
249    };
250}