facet_core/impls/core/
array.rs

1//! Facet implementation for [T; N] arrays
2
3use core::{cmp::Ordering, fmt};
4
5use crate::{
6    ArrayDef, ArrayVTable, Def, Facet, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut,
7    Shape, ShapeBuilder, Type, TypeNameOpts, TypeOpsIndirect, TypeParam, VTableIndirect,
8};
9
10/// Extract the ArrayDef from a shape, returns None if not an array
11#[inline]
12fn get_array_def(shape: &'static Shape) -> Option<&'static ArrayDef> {
13    match shape.def {
14        Def::Array(ref def) => Some(def),
15        _ => None,
16    }
17}
18
19/// Type-erased type_name for arrays - reads T and N from the shape
20fn array_type_name(
21    shape: &'static Shape,
22    f: &mut fmt::Formatter<'_>,
23    opts: TypeNameOpts,
24) -> fmt::Result {
25    let def = match &shape.def {
26        Def::Array(def) => def,
27        _ => return write!(f, "[?; ?]"),
28    };
29
30    if let Some(opts) = opts.for_children() {
31        write!(f, "[")?;
32        def.t.write_type_name(f, opts)?;
33        write!(f, "; {}]", def.n)
34    } else {
35        write!(f, "[…; {}]", def.n)
36    }
37}
38
39/// Debug for [T; N] - formats as array literal
40unsafe fn array_debug(
41    ox: OxPtrConst,
42    f: &mut core::fmt::Formatter<'_>,
43) -> Option<core::fmt::Result> {
44    let shape = ox.shape();
45    let def = get_array_def(shape)?;
46    let ptr = ox.ptr();
47
48    let mut list = f.debug_list();
49    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
50    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
51
52    for i in 0..def.n {
53        let elem_ptr = unsafe { PtrConst::new((slice_ptr.as_byte_ptr()).add(i * stride)) };
54        let elem_ox = OxRef::new(elem_ptr, def.t);
55        list.entry(&elem_ox);
56    }
57    Some(list.finish())
58}
59
60/// Hash for [T; N] - hashes each element
61unsafe fn array_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
62    let shape = ox.shape();
63    let def = get_array_def(shape)?;
64    let ptr = ox.ptr();
65
66    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
67    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
68
69    for i in 0..def.n {
70        let elem_ptr = unsafe { PtrConst::new((slice_ptr.as_byte_ptr()).add(i * stride)) };
71        unsafe { def.t.call_hash(elem_ptr, hasher)? };
72    }
73    Some(())
74}
75
76/// PartialEq for [T; N]
77unsafe fn array_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
78    let shape = a.shape();
79    let def = get_array_def(shape)?;
80
81    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
82    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
83    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
84
85    for i in 0..def.n {
86        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
87        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
88        if !unsafe { def.t.call_partial_eq(a_elem, b_elem)? } {
89            return Some(false);
90        }
91    }
92    Some(true)
93}
94
95/// PartialOrd for [T; N]
96unsafe fn array_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
97    let shape = a.shape();
98    let def = get_array_def(shape)?;
99
100    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
101    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
102    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
103
104    for i in 0..def.n {
105        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
106        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
107        match unsafe { def.t.call_partial_cmp(a_elem, b_elem)? } {
108            Some(Ordering::Equal) => continue,
109            other => return Some(other),
110        }
111    }
112    Some(Some(Ordering::Equal))
113}
114
115/// Ord for [T; N]
116unsafe fn array_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
117    let shape = a.shape();
118    let def = get_array_def(shape)?;
119
120    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
121    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
122    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
123
124    for i in 0..def.n {
125        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
126        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
127        match unsafe { def.t.call_cmp(a_elem, b_elem)? } {
128            Ordering::Equal => continue,
129            other => return Some(other),
130        }
131    }
132    Some(Ordering::Equal)
133}
134
135/// Drop for [T; N]
136unsafe fn array_drop(ox: OxPtrMut) {
137    let shape = ox.shape();
138    let Some(def) = get_array_def(shape) else {
139        return;
140    };
141    let ptr = ox.ptr();
142
143    let slice_ptr = unsafe { (def.vtable.as_mut_ptr)(ptr) };
144    let Some(stride) = def
145        .t
146        .layout
147        .sized_layout()
148        .ok()
149        .map(|l| l.pad_to_align().size())
150    else {
151        return;
152    };
153
154    for i in 0..def.n {
155        let elem_ptr = unsafe { PtrMut::new((slice_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
156        unsafe { def.t.call_drop_in_place(elem_ptr) };
157    }
158}
159
160/// Default for [T; N] - default-initializes each element
161unsafe fn array_default(ox: OxPtrMut) {
162    let shape = ox.shape();
163    let Some(def) = get_array_def(shape) else {
164        return;
165    };
166    let ptr = ox.ptr();
167
168    let slice_ptr = unsafe { (def.vtable.as_mut_ptr)(ptr) };
169    let Some(stride) = def
170        .t
171        .layout
172        .sized_layout()
173        .ok()
174        .map(|l| l.pad_to_align().size())
175    else {
176        return;
177    };
178
179    for i in 0..def.n {
180        let elem_ptr = unsafe { PtrMut::new((slice_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
181        if unsafe { def.t.call_default_in_place(elem_ptr) }.is_none() {
182            return;
183        }
184    }
185}
186
187/// Clone for [T; N] - clones each element
188unsafe fn array_clone(src: OxPtrConst, dst: OxPtrMut) {
189    let shape = src.shape();
190    let Some(def) = get_array_def(shape) else {
191        return;
192    };
193
194    let src_ptr = unsafe { (def.vtable.as_ptr)(src.ptr()) };
195    let dst_ptr = unsafe { (def.vtable.as_mut_ptr)(dst.ptr()) };
196    let Some(stride) = def
197        .t
198        .layout
199        .sized_layout()
200        .ok()
201        .map(|l| l.pad_to_align().size())
202    else {
203        return;
204    };
205
206    for i in 0..def.n {
207        let src_elem = unsafe { PtrConst::new((src_ptr.as_byte_ptr()).add(i * stride)) };
208        let dst_elem = unsafe { PtrMut::new((dst_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
209        if unsafe { def.t.call_clone_into(src_elem, dst_elem) }.is_none() {
210            return;
211        }
212    }
213}
214
215// Shared vtable for all [T; N]
216const ARRAY_VTABLE: VTableIndirect = VTableIndirect {
217    display: None,
218    debug: Some(array_debug),
219    hash: Some(array_hash),
220    invariants: None,
221    parse: None,
222    parse_bytes: None,
223    try_from: None,
224    try_into_inner: None,
225    try_borrow_inner: None,
226    partial_eq: Some(array_partial_eq),
227    partial_cmp: Some(array_partial_cmp),
228    cmp: Some(array_cmp),
229};
230
231/// Get pointer to array data buffer
232unsafe fn array_as_ptr<T, const N: usize>(ptr: PtrConst) -> PtrConst {
233    let array = unsafe { ptr.get::<[T; N]>() };
234    PtrConst::new(array.as_ptr() as *const u8)
235}
236
237/// Get mutable pointer to array data buffer
238unsafe fn array_as_mut_ptr<T, const N: usize>(ptr: PtrMut) -> PtrMut {
239    let array = unsafe { ptr.as_mut::<[T; N]>() };
240    PtrMut::new(array.as_mut_ptr() as *mut u8)
241}
242
243unsafe impl<'a, T, const N: usize> Facet<'a> for [T; N]
244where
245    T: Facet<'a>,
246{
247    const SHAPE: &'static Shape = &const {
248        const fn build_array_vtable<T, const N: usize>() -> ArrayVTable {
249            ArrayVTable::builder()
250                .as_ptr(array_as_ptr::<T, N>)
251                .as_mut_ptr(array_as_mut_ptr::<T, N>)
252                .build()
253        }
254
255        ShapeBuilder::for_sized::<[T; N]>("[T; N]")
256            .type_name(array_type_name)
257            .ty(Type::Sequence(crate::SequenceType::Array(
258                crate::ArrayType { t: T::SHAPE, n: N },
259            )))
260            .def(Def::Array(ArrayDef::new(
261                &const { build_array_vtable::<T, N>() },
262                T::SHAPE,
263                N,
264            )))
265            .type_params(&[TypeParam {
266                name: "T",
267                shape: T::SHAPE,
268            }])
269            .vtable_indirect(&ARRAY_VTABLE)
270            .type_ops_indirect(
271                &const {
272                    unsafe fn truthy<const N: usize>(_: PtrConst) -> bool {
273                        N != 0
274                    }
275
276                    TypeOpsIndirect {
277                        drop_in_place: array_drop,
278                        default_in_place: Some(array_default),
279                        clone_into: Some(array_clone),
280                        is_truthy: Some(truthy::<N>),
281                    }
282                },
283            )
284            .build()
285    };
286}