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        let elem_ptr = unsafe { PtrConst::new((slice_ptr.raw_ptr()).add(i * stride)) };
79        let elem_ox = OxRef::new(elem_ptr, def.t);
80        list.entry(&elem_ox);
81    }
82    Some(list.finish())
83}
84
85/// Hash for `[T]`
86unsafe fn slice_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
87    let shape = ox.shape();
88    let def = get_slice_def(shape)?;
89    let ptr = ox.ptr();
90
91    let len = unsafe { (def.vtable.len)(ptr) };
92    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
93    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
94
95    for i in 0..len {
96        let elem_ptr = unsafe { PtrConst::new((slice_ptr.raw_ptr()).add(i * stride)) };
97        unsafe { def.t.call_hash(elem_ptr, hasher)? };
98    }
99    Some(())
100}
101
102/// PartialEq for `[T]`
103unsafe fn slice_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
104    let shape = a.shape();
105    let def = get_slice_def(shape)?;
106
107    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
108    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
109
110    if a_len != b_len {
111        return Some(false);
112    }
113
114    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
115    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
116    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
117
118    for i in 0..a_len {
119        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
120        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
121        if !unsafe { def.t.call_partial_eq(a_elem, b_elem)? } {
122            return Some(false);
123        }
124    }
125    Some(true)
126}
127
128/// PartialOrd for `[T]`
129unsafe fn slice_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
130    let shape = a.shape();
131    let def = get_slice_def(shape)?;
132
133    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
134    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
135
136    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
137    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
138    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
139
140    let min_len = a_len.min(b_len);
141    for i in 0..min_len {
142        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
143        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
144        match unsafe { def.t.call_partial_cmp(a_elem, b_elem)? } {
145            Some(Ordering::Equal) => continue,
146            other => return Some(other),
147        }
148    }
149    Some(Some(a_len.cmp(&b_len)))
150}
151
152/// Ord for `[T]`
153unsafe fn slice_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
154    let shape = a.shape();
155    let def = get_slice_def(shape)?;
156
157    let a_len = unsafe { (def.vtable.len)(a.ptr()) };
158    let b_len = unsafe { (def.vtable.len)(b.ptr()) };
159
160    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
161    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
162    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
163
164    let min_len = a_len.min(b_len);
165    for i in 0..min_len {
166        let a_elem = unsafe { PtrConst::new((a_ptr.raw_ptr()).add(i * stride)) };
167        let b_elem = unsafe { PtrConst::new((b_ptr.raw_ptr()).add(i * stride)) };
168        match unsafe { def.t.call_cmp(a_elem, b_elem)? } {
169            Ordering::Equal => continue,
170            other => return Some(other),
171        }
172    }
173    Some(a_len.cmp(&b_len))
174}
175
176// Shared vtable for all [T]
177const SLICE_VTABLE: VTableIndirect = VTableIndirect {
178    display: None,
179    debug: Some(slice_debug),
180    hash: Some(slice_hash),
181    invariants: None,
182    parse: None,
183    parse_bytes: None,
184    try_from: None,
185    try_into_inner: None,
186    try_borrow_inner: None,
187    partial_eq: Some(slice_partial_eq),
188    partial_cmp: Some(slice_partial_cmp),
189    cmp: Some(slice_cmp),
190};
191
192unsafe impl<'a, T> Facet<'a> for [T]
193where
194    T: Facet<'a>,
195{
196    const SHAPE: &'static Shape = &const {
197        const fn build_type_name<'a, T: Facet<'a>>() -> TypeNameFn {
198            fn type_name_impl<'a, T: Facet<'a>>(
199                _shape: &'static Shape,
200                f: &mut core::fmt::Formatter<'_>,
201                opts: TypeNameOpts,
202            ) -> core::fmt::Result {
203                if let Some(opts) = opts.for_children() {
204                    write!(f, "[")?;
205                    T::SHAPE.write_type_name(f, opts)?;
206                    write!(f, "]")
207                } else {
208                    write!(f, "[…]")
209                }
210            }
211            type_name_impl::<T>
212        }
213
214        const fn build_slice_vtable<'a, T: Facet<'a>>() -> SliceVTable {
215            SliceVTable {
216                len: slice_len::<T>,
217                as_ptr: slice_as_ptr::<T>,
218                as_mut_ptr: slice_as_mut_ptr::<T>,
219            }
220        }
221
222        ShapeBuilder::for_unsized::<Self>("[_]")
223            .type_name(build_type_name::<T>())
224            .vtable_indirect(&SLICE_VTABLE)
225            .ty(Type::Sequence(SequenceType::Slice(SliceType {
226                t: T::SHAPE,
227            })))
228            .def(Def::Slice(SliceDef::new(
229                &const { build_slice_vtable::<T>() },
230                T::SHAPE,
231            )))
232            .type_params(&[TypeParam {
233                name: "T",
234                shape: T::SHAPE,
235            }])
236            .type_ops_indirect(
237                &const {
238                    TypeOpsIndirect {
239                        drop_in_place: slice_drop,
240                        default_in_place: None,
241                        clone_into: None,
242                        is_truthy: Some(slice_truthy::<T>),
243                    }
244                },
245            )
246            .build()
247    };
248}