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    try_from: None,
223    try_into_inner: None,
224    try_borrow_inner: None,
225    partial_eq: Some(array_partial_eq),
226    partial_cmp: Some(array_partial_cmp),
227    cmp: Some(array_cmp),
228};
229
230/// Get pointer to array data buffer
231unsafe fn array_as_ptr<T, const N: usize>(ptr: PtrConst) -> PtrConst {
232    let array = unsafe { ptr.get::<[T; N]>() };
233    PtrConst::new(array.as_ptr() as *const u8)
234}
235
236/// Get mutable pointer to array data buffer
237unsafe fn array_as_mut_ptr<T, const N: usize>(ptr: PtrMut) -> PtrMut {
238    let array = unsafe { ptr.as_mut::<[T; N]>() };
239    PtrMut::new(array.as_mut_ptr() as *mut u8)
240}
241
242unsafe impl<'a, T, const N: usize> Facet<'a> for [T; N]
243where
244    T: Facet<'a>,
245{
246    const SHAPE: &'static Shape = &const {
247        const fn build_array_vtable<T, const N: usize>() -> ArrayVTable {
248            ArrayVTable::builder()
249                .as_ptr(array_as_ptr::<T, N>)
250                .as_mut_ptr(array_as_mut_ptr::<T, N>)
251                .build()
252        }
253
254        ShapeBuilder::for_sized::<[T; N]>("[T; N]")
255            .type_name(array_type_name)
256            .ty(Type::Sequence(crate::SequenceType::Array(
257                crate::ArrayType { t: T::SHAPE, n: N },
258            )))
259            .def(Def::Array(ArrayDef::new(
260                &const { build_array_vtable::<T, N>() },
261                T::SHAPE,
262                N,
263            )))
264            .type_params(&[TypeParam {
265                name: "T",
266                shape: T::SHAPE,
267            }])
268            .vtable_indirect(&ARRAY_VTABLE)
269            .type_ops_indirect(
270                &const {
271                    unsafe fn truthy<const N: usize>(_: PtrConst) -> bool {
272                        N != 0
273                    }
274
275                    TypeOpsIndirect {
276                        drop_in_place: array_drop,
277                        default_in_place: Some(array_default),
278                        clone_into: Some(array_clone),
279                        is_truthy: Some(truthy::<N>),
280                    }
281                },
282            )
283            .build()
284    };
285}