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