facet_reflect/peek/
list_like.rs

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