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().thin().unwrap()) },
125            ListLikeDef::Slice(v) => unsafe { (v.vtable.len)(value.data().thin().unwrap()) },
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    /// Return `None` if the index is out of bounds
144    pub fn get(&self, index: usize) -> Option<Peek<'mem, 'facet, 'shape>> {
145        let as_ptr = match self.def {
146            ListLikeDef::List(def) => {
147                // Call get from the list's vtable directly if available
148                let item = unsafe { (def.vtable.get)(self.value.data().thin().unwrap(), index)? };
149                return Some(unsafe { Peek::unchecked_new(item, self.def.t()) });
150            }
151            ListLikeDef::Array(def) => def.vtable.as_ptr,
152            ListLikeDef::Slice(def) => def.vtable.as_ptr,
153        };
154
155        if index >= self.len() {
156            return None;
157        }
158
159        // Get the base pointer of the array
160        let base_ptr = unsafe { as_ptr(self.value.data().thin().unwrap()) };
161
162        // Get the layout of the element type
163        let elem_layout = match self.def.t().layout {
164            ShapeLayout::Sized(layout) => layout,
165            ShapeLayout::Unsized => return None, // Cannot handle unsized elements
166        };
167
168        // Calculate the offset based on element size
169        let offset = index * elem_layout.size();
170
171        // Apply the offset to get the item's pointer
172        let item_ptr = unsafe { base_ptr.field(offset) };
173
174        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
175    }
176
177    /// Returns an iterator over the list
178    pub fn iter(self) -> PeekListLikeIter<'mem, 'facet, 'shape> {
179        let (as_ptr_fn, iter_vtable) = match self.def {
180            ListLikeDef::List(def) => (def.vtable.as_ptr, Some(def.vtable.iter_vtable)),
181            ListLikeDef::Array(def) => (Some(def.vtable.as_ptr), None),
182            ListLikeDef::Slice(def) => (Some(def.vtable.as_ptr), None),
183        };
184
185        let state = match (as_ptr_fn, iter_vtable) {
186            (Some(as_ptr_fn), _) => {
187                let data = unsafe { as_ptr_fn(self.value.data().thin().unwrap()) };
188                let layout = self
189                    .def
190                    .t()
191                    .layout
192                    .sized_layout()
193                    .expect("can only iterate over sized list elements");
194                let stride = layout.size();
195
196                PeekListLikeIterState::Ptr { data, stride }
197            }
198            (None, Some(vtable)) => {
199                let iter =
200                    unsafe { (vtable.init_with_value.unwrap())(self.value.data().thin().unwrap()) };
201                PeekListLikeIterState::Iter { iter, vtable }
202            }
203            (None, None) => unreachable!(),
204        };
205
206        PeekListLikeIter {
207            state,
208            index: 0,
209            len: self.len(),
210            def: self.def(),
211            _list: PhantomData,
212        }
213    }
214
215    /// Def getter
216    pub fn def(&self) -> ListLikeDef<'shape> {
217        self.def
218    }
219}