facet_reflect/peek/
list_like.rs

1use facet_core::{IterVTable, PtrConst, PtrMut, Shape, ShapeLayout};
2
3use super::Peek;
4use core::{fmt::Debug, marker::PhantomData, ptr::NonNull};
5
6/// Fields for types which act like lists
7#[derive(Clone, Copy)]
8pub enum ListLikeDef {
9    /// Ordered list of homogenous values, variable size
10    ///
11    /// e.g. `Vec<T>`
12    List(facet_core::ListDef),
13
14    /// Fixed-size array of homogenous values
15    ///
16    /// e.g. `[T; 32]`
17    Array(facet_core::ArrayDef),
18
19    /// Slice — a contiguous sequence of elements
20    ///
21    /// e.g. `[T]`
22    Slice(facet_core::SliceDef),
23}
24
25impl ListLikeDef {
26    /// Returns the shape of the items in the list
27    #[inline]
28    pub fn t(&self) -> &'static Shape {
29        match self {
30            ListLikeDef::List(v) => v.t(),
31            ListLikeDef::Array(v) => v.t(),
32            ListLikeDef::Slice(v) => v.t(),
33        }
34    }
35}
36
37/// Iterator over a `PeekListLike`
38pub struct PeekListLikeIter<'mem, 'facet> {
39    state: PeekListLikeIterState<'mem>,
40    index: usize,
41    len: usize,
42    def: ListLikeDef,
43    _list: PhantomData<Peek<'mem, 'facet>>,
44}
45
46impl<'mem, 'facet> Iterator for PeekListLikeIter<'mem, 'facet> {
47    type Item = Peek<'mem, 'facet>;
48
49    #[inline]
50    fn next(&mut self) -> Option<Self::Item> {
51        let item_ptr = match &self.state.kind {
52            PeekListLikeIterStateKind::Ptr { data, stride } => {
53                if self.index >= self.len {
54                    return None;
55                }
56
57                unsafe { data.field(stride * self.index) }
58            }
59            PeekListLikeIterStateKind::Iter { iter, vtable } => unsafe { (vtable.next)(*iter)? },
60        };
61
62        // Update the index. This is used pointer iteration and for
63        // calculating the iterator's size
64        self.index += 1;
65
66        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
67    }
68
69    #[inline]
70    fn size_hint(&self) -> (usize, Option<usize>) {
71        let remaining = self.len.saturating_sub(self.index);
72        (remaining, Some(remaining))
73    }
74}
75
76impl<'mem, 'facet> ExactSizeIterator for PeekListLikeIter<'mem, 'facet> {}
77
78impl<'mem, 'facet> IntoIterator for &'mem PeekListLike<'mem, 'facet> {
79    type Item = Peek<'mem, 'facet>;
80    type IntoIter = PeekListLikeIter<'mem, 'facet>;
81
82    #[inline]
83    fn into_iter(self) -> Self::IntoIter {
84        self.iter()
85    }
86}
87
88struct PeekListLikeIterState<'mem> {
89    kind: PeekListLikeIterStateKind,
90    _phantom: PhantomData<&'mem ()>,
91}
92
93enum PeekListLikeIterStateKind {
94    Ptr {
95        data: PtrConst,
96        stride: usize,
97    },
98    Iter {
99        iter: PtrMut,
100        vtable: &'static IterVTable<PtrConst>,
101    },
102}
103
104impl Drop for PeekListLikeIterState<'_> {
105    #[inline]
106    fn drop(&mut self) {
107        match &self.kind {
108            PeekListLikeIterStateKind::Iter { iter, vtable } => unsafe { (vtable.dealloc)(*iter) },
109            PeekListLikeIterStateKind::Ptr { .. } => {
110                // Nothing to do
111            }
112        }
113    }
114}
115
116/// Lets you read from a list, array or slice
117#[derive(Clone, Copy)]
118pub struct PeekListLike<'mem, 'facet> {
119    pub(crate) value: Peek<'mem, 'facet>,
120    pub(crate) def: ListLikeDef,
121    len: usize,
122}
123
124impl<'mem, 'facet> Debug for PeekListLike<'mem, 'facet> {
125    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
126        f.debug_struct("PeekListLike").finish_non_exhaustive()
127    }
128}
129
130impl<'mem, 'facet> PeekListLike<'mem, 'facet> {
131    /// Creates a new peek list
132    #[inline]
133    pub fn new(value: Peek<'mem, 'facet>, def: ListLikeDef) -> Self {
134        let len = match def {
135            ListLikeDef::List(v) => unsafe { (v.vtable.len)(value.data()) },
136            ListLikeDef::Slice(_) => {
137                let slice_as_units = unsafe { value.data.get::<[()]>() };
138                slice_as_units.len()
139            }
140            ListLikeDef::Array(v) => v.n,
141        };
142        Self { value, def, len }
143    }
144
145    /// Get the length of the list
146    #[inline]
147    pub fn len(&self) -> usize {
148        self.len
149    }
150
151    /// Returns true if the list is empty
152    #[inline]
153    pub fn is_empty(&self) -> bool {
154        self.len() == 0
155    }
156
157    /// Get an item from the list at the specified index
158    ///
159    /// Return `None` if the index is out of bounds
160    pub fn get(&self, index: usize) -> Option<Peek<'mem, 'facet>> {
161        // Special handling for bare slices with wide pointers
162        if let ListLikeDef::Slice(_) = &self.def {
163            if index >= self.len() {
164                return None;
165            }
166
167            // Get the element type layout
168            let elem_layout = match self.def.t().layout {
169                ShapeLayout::Sized(layout) => layout,
170                ShapeLayout::Unsized => return None,
171            };
172
173            // Get the data pointer directly from the wide pointer
174            let data_ptr = self.value.data().raw_ptr();
175
176            // Calculate the element pointer
177            let elem_ptr = unsafe { data_ptr.add(index * elem_layout.size()) };
178
179            // Create a Peek for the element
180            return Some(unsafe {
181                Peek::unchecked_new(
182                    PtrConst::new(NonNull::new_unchecked(elem_ptr as *mut u8).as_ptr()),
183                    self.def.t(),
184                )
185            });
186        }
187
188        let as_ptr = match self.def {
189            ListLikeDef::List(def) => {
190                // Call get from the list's vtable directly if available
191                let item =
192                    unsafe { (def.vtable.get)(self.value.data(), index, self.value.shape())? };
193                return Some(unsafe { Peek::unchecked_new(item, self.def.t()) });
194            }
195            ListLikeDef::Array(def) => def.vtable.as_ptr,
196            ListLikeDef::Slice(def) => def.vtable.as_ptr,
197        };
198
199        if index >= self.len() {
200            return None;
201        }
202
203        // Get the base pointer of the array
204        let base_ptr = unsafe { as_ptr(self.value.data()) };
205
206        // Get the layout of the element type
207        let elem_layout = match self.def.t().layout {
208            ShapeLayout::Sized(layout) => layout,
209            ShapeLayout::Unsized => return None, // Cannot handle unsized elements
210        };
211
212        // Calculate the offset based on element size
213        let offset = index * elem_layout.size();
214
215        // Apply the offset to get the item's pointer
216        let item_ptr = unsafe { base_ptr.field(offset) };
217
218        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
219    }
220
221    /// Returns an iterator over the list
222    pub fn iter(self) -> PeekListLikeIter<'mem, 'facet> {
223        let (as_ptr_fn, iter_vtable) = match self.def {
224            ListLikeDef::List(def) => (def.vtable.as_ptr, def.iter_vtable()),
225            ListLikeDef::Array(def) => (Some(def.vtable.as_ptr), None),
226            ListLikeDef::Slice(def) => (Some(def.vtable.as_ptr), None),
227        };
228
229        let state = match (as_ptr_fn, iter_vtable) {
230            (Some(as_ptr_fn), _) => {
231                // Special handling for bare slices with wide pointers
232                let data = if let ListLikeDef::Slice(_) = &self.def {
233                    // Get the data pointer directly from the wide pointer
234                    PtrConst::new(unsafe {
235                        NonNull::new_unchecked(self.value.data().raw_ptr() as *mut u8).as_ptr()
236                    })
237                } else {
238                    unsafe { as_ptr_fn(self.value.data()) }
239                };
240
241                let layout = self
242                    .def
243                    .t()
244                    .layout
245                    .sized_layout()
246                    .expect("can only iterate over sized list elements");
247                let stride = layout.size();
248
249                PeekListLikeIterState {
250                    kind: PeekListLikeIterStateKind::Ptr { data, stride },
251                    _phantom: PhantomData,
252                }
253            }
254            (None, Some(vtable)) => {
255                let iter = unsafe { (vtable.init_with_value.unwrap())(self.value.data()) };
256                PeekListLikeIterState {
257                    kind: PeekListLikeIterStateKind::Iter { iter, vtable },
258                    _phantom: PhantomData,
259                }
260            }
261            (None, None) => unreachable!(),
262        };
263
264        PeekListLikeIter {
265            state,
266            index: 0,
267            len: self.len(),
268            def: self.def(),
269            _list: PhantomData,
270        }
271    }
272
273    /// Def getter
274    #[inline]
275    pub fn def(&self) -> ListLikeDef {
276        self.def
277    }
278}