facet_reflect/peek/
list_like.rs

1use facet_core::{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    pub fn t(&self) -> &'shape Shape<'shape> {
28        match self {
29            ListLikeDef::List(v) => v.t(),
30            ListLikeDef::Array(v) => v.t(),
31            ListLikeDef::Slice(v) => v.t(),
32        }
33    }
34}
35
36/// Iterator over a `PeekListLike`
37pub struct PeekListLikeIter<'mem, 'facet, 'shape> {
38    state: PeekListLikeIterState<'mem>,
39    index: usize,
40    len: usize,
41    def: ListLikeDef<'shape>,
42    _list: PhantomData<Peek<'mem, 'facet, 'shape>>,
43}
44
45impl<'mem, 'facet, 'shape> Iterator for PeekListLikeIter<'mem, 'facet, 'shape> {
46    type Item = Peek<'mem, 'facet, 'shape>;
47
48    fn next(&mut self) -> Option<Self::Item> {
49        let item_ptr = match self.state {
50            PeekListLikeIterState::Ptr { data, stride } => {
51                if self.index >= self.len {
52                    return None;
53                }
54
55                unsafe { data.field(stride * self.index) }
56            }
57            PeekListLikeIterState::Iter { iter, vtable } => unsafe { (vtable.next)(iter)? },
58        };
59
60        // Update the index. This is used pointer iteration and for
61        // calculating the iterator's size
62        self.index += 1;
63
64        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
65    }
66
67    fn size_hint(&self) -> (usize, Option<usize>) {
68        let remaining = self.len.saturating_sub(self.index);
69        (remaining, Some(remaining))
70    }
71}
72
73impl<'mem, 'facet, 'shape> ExactSizeIterator for PeekListLikeIter<'mem, 'facet, 'shape> {}
74
75impl<'mem, 'facet, 'shape> IntoIterator for &'mem PeekListLike<'mem, 'facet, 'shape> {
76    type Item = Peek<'mem, 'facet, 'shape>;
77    type IntoIter = PeekListLikeIter<'mem, 'facet, 'shape>;
78
79    fn into_iter(self) -> Self::IntoIter {
80        self.iter()
81    }
82}
83
84enum PeekListLikeIterState<'mem> {
85    Ptr {
86        data: PtrConst<'mem>,
87        stride: usize,
88    },
89    Iter {
90        iter: PtrMut<'mem>,
91        vtable: IterVTable<PtrConst<'static>>,
92    },
93}
94
95impl Drop for PeekListLikeIterState<'_> {
96    fn drop(&mut self) {
97        match self {
98            Self::Iter { iter, vtable } => unsafe { (vtable.dealloc)(*iter) },
99            Self::Ptr { .. } => {
100                // Nothing to do
101            }
102        }
103    }
104}
105
106/// Lets you read from a list, array or slice
107#[derive(Clone, Copy)]
108pub struct PeekListLike<'mem, 'facet, 'shape> {
109    pub(crate) value: Peek<'mem, 'facet, 'shape>,
110    pub(crate) def: ListLikeDef<'shape>,
111    len: usize,
112}
113
114impl<'mem, 'facet, 'shape> Debug for PeekListLike<'mem, 'facet, 'shape> {
115    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116        f.debug_struct("PeekListLike").finish_non_exhaustive()
117    }
118}
119
120impl<'mem, 'facet, 'shape> PeekListLike<'mem, 'facet, 'shape> {
121    /// Creates a new peek list
122    pub fn new(value: Peek<'mem, 'facet, 'shape>, def: ListLikeDef<'shape>) -> Self {
123        let len = match def {
124            ListLikeDef::List(v) => unsafe { (v.vtable.len)(value.data()) },
125            ListLikeDef::Slice(v) => unsafe { (v.vtable.len)(value.data()) },
126            ListLikeDef::Array(v) => v.n,
127        };
128        Self { value, def, len }
129    }
130
131    /// Get the length of the list
132    pub fn len(&self) -> usize {
133        self.len
134    }
135
136    /// Returns true if the list is empty
137    pub fn is_empty(&self) -> bool {
138        self.len() == 0
139    }
140
141    /// Get an item from the list at the specified index
142    ///
143    /// # Panics
144    ///
145    /// Panics if the index is out of bounds
146    pub fn get(&self, index: usize) -> Option<Peek<'mem, 'facet, 'shape>> {
147        let as_ptr = match self.def {
148            ListLikeDef::List(def) => {
149                // Call get from the list's vtable directly if available
150                let item = unsafe { (def.vtable.get)(self.value.data(), index)? };
151                return Some(unsafe { Peek::unchecked_new(item, self.def.t()) });
152            }
153            ListLikeDef::Array(def) => def.vtable.as_ptr,
154            ListLikeDef::Slice(def) => def.vtable.as_ptr,
155        };
156
157        if index >= self.len() {
158            return None;
159        }
160
161        // Get the base pointer of the array
162        let base_ptr = unsafe { as_ptr(self.value.data()) };
163
164        // Get the layout of the element type
165        let elem_layout = match self.def.t().layout {
166            ShapeLayout::Sized(layout) => layout,
167            ShapeLayout::Unsized => return None, // Cannot handle unsized elements
168        };
169
170        // Calculate the offset based on element size
171        let offset = index * elem_layout.size();
172
173        // Apply the offset to get the item's pointer
174        let item_ptr = unsafe { base_ptr.field(offset) };
175
176        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
177    }
178
179    /// Returns an iterator over the list
180    pub fn iter(self) -> PeekListLikeIter<'mem, 'facet, 'shape> {
181        let (as_ptr_fn, iter_vtable) = match self.def {
182            ListLikeDef::List(def) => (def.vtable.as_ptr, Some(def.vtable.iter_vtable)),
183            ListLikeDef::Array(def) => (Some(def.vtable.as_ptr), None),
184            ListLikeDef::Slice(def) => (Some(def.vtable.as_ptr), None),
185        };
186
187        let state = match (as_ptr_fn, iter_vtable) {
188            (Some(as_ptr_fn), _) => {
189                let data = unsafe { as_ptr_fn(self.value.data()) };
190                let layout = self
191                    .def
192                    .t()
193                    .layout
194                    .sized_layout()
195                    .expect("can only iterate over sized list elements");
196                let stride = layout.size();
197
198                PeekListLikeIterState::Ptr { data, stride }
199            }
200            (None, Some(vtable)) => {
201                let iter = unsafe { (vtable.init_with_value.unwrap())(self.value.data()) };
202                PeekListLikeIterState::Iter { iter, vtable }
203            }
204            (None, None) => unreachable!(),
205        };
206
207        PeekListLikeIter {
208            state,
209            index: 0,
210            len: self.len(),
211            def: self.def(),
212            _list: PhantomData,
213        }
214    }
215
216    /// Def getter
217    pub fn def(&self) -> ListLikeDef<'shape> {
218        self.def
219    }
220}