Skip to main content

facet_reflect/poke/
list.rs

1use super::Poke;
2use core::{fmt::Debug, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
3use facet_core::{FieldError, ListDef, PtrMut, PtrUninit, Shape};
4
5use crate::{Guard, HeapValue, ReflectError, ReflectErrorKind};
6
7/// Iterator over a `PokeList` yielding mutable `Poke`s.
8///
9/// Constructed by [`PokeList::iter_mut`]. Walks element strides starting from the list's
10/// `as_mut_ptr`; `iter_mut` refuses to build one if that entry is missing or the element
11/// type is unsized.
12pub struct PokeListIter<'mem, 'facet> {
13    data: PtrMut,
14    stride: usize,
15    index: usize,
16    len: usize,
17    elem_shape: &'static Shape,
18    _list: PhantomData<Poke<'mem, 'facet>>,
19}
20
21impl<'mem, 'facet> Iterator for PokeListIter<'mem, 'facet> {
22    type Item = Poke<'mem, 'facet>;
23
24    #[inline]
25    fn next(&mut self) -> Option<Self::Item> {
26        if self.index >= self.len {
27            return None;
28        }
29        let item_ptr = unsafe { self.data.field(self.stride * self.index) };
30        self.index += 1;
31        Some(unsafe { Poke::from_raw_parts(item_ptr, self.elem_shape) })
32    }
33
34    #[inline]
35    fn size_hint(&self) -> (usize, Option<usize>) {
36        let remaining = self.len.saturating_sub(self.index);
37        (remaining, Some(remaining))
38    }
39}
40
41impl ExactSizeIterator for PokeListIter<'_, '_> {}
42
43/// Lets you mutate a list (implements mutable [`facet_core::ListVTable`] proxies)
44pub struct PokeList<'mem, 'facet> {
45    value: Poke<'mem, 'facet>,
46    def: ListDef,
47}
48
49impl Debug for PokeList<'_, '_> {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        f.debug_struct("PokeList").finish_non_exhaustive()
52    }
53}
54
55impl<'mem, 'facet> PokeList<'mem, 'facet> {
56    /// Creates a new poke list
57    ///
58    /// # Safety
59    ///
60    /// The caller must ensure that `def` contains valid vtable function pointers that:
61    /// - Correctly implement the list operations for the actual type
62    /// - Do not cause undefined behavior when called
63    /// - Return pointers within valid memory bounds
64    /// - Match the element type specified in `def.t()`
65    ///
66    /// Violating these requirements can lead to memory safety issues.
67    #[inline]
68    pub const unsafe fn new(value: Poke<'mem, 'facet>, def: ListDef) -> Self {
69        Self { value, def }
70    }
71
72    /// Get the length of the list
73    #[inline]
74    pub fn len(&self) -> usize {
75        unsafe { (self.def.vtable.len)(self.value.data()) }
76    }
77
78    /// Returns true if the list is empty
79    #[inline]
80    pub fn is_empty(&self) -> bool {
81        self.len() == 0
82    }
83
84    /// Get an immutable reference to an item from the list at the specified index
85    #[inline]
86    pub fn get(&self, index: usize) -> Option<crate::Peek<'_, 'facet>> {
87        let item = unsafe { (self.def.vtable.get)(self.value.data(), index, self.value.shape())? };
88
89        Some(unsafe { crate::Peek::unchecked_new(item, self.def.t()) })
90    }
91
92    /// Get a mutable reference to an item from the list at the specified index
93    #[inline]
94    pub fn get_mut(&mut self, index: usize) -> Option<Poke<'_, 'facet>> {
95        let get_mut_fn = self.def.vtable.get_mut?;
96        let item = unsafe { get_mut_fn(self.value.data, index, self.value.shape())? };
97
98        Some(unsafe { Poke::from_raw_parts(item, self.def.t()) })
99    }
100
101    /// Returns a mutable iterator over the list.
102    ///
103    /// Requires contiguous mutable access: the element type must be sized and the list
104    /// vtable must expose `as_mut_ptr`. Returns [`ReflectErrorKind::OperationFailed`] otherwise;
105    /// use [`PokeList::get_mut`] per index when `iter_mut` is unavailable.
106    ///
107    /// The previous fallback that synthesized a mutable iterator from the list's `iter_vtable`
108    /// was unsound: that vtable yields `PtrConst` items backed by shared references, and writing
109    /// through them is UB.
110    pub fn iter_mut(self) -> Result<PokeListIter<'mem, 'facet>, ReflectError> {
111        let elem_shape = self.def.t();
112        let stride = match elem_shape.layout {
113            facet_core::ShapeLayout::Sized(layout) => layout.size(),
114            facet_core::ShapeLayout::Unsized => {
115                return Err(self.value.err(ReflectErrorKind::OperationFailed {
116                    shape: self.value.shape(),
117                    operation: "iter_mut requires sized element type",
118                }));
119            }
120        };
121
122        let Some(as_mut_ptr_fn) = self.def.vtable.as_mut_ptr else {
123            return Err(self.value.err(ReflectErrorKind::OperationFailed {
124                shape: self.value.shape(),
125                operation:
126                    "iter_mut requires a contiguous `as_mut_ptr` vtable entry; use `get_mut` per index",
127            }));
128        };
129
130        let data = unsafe { as_mut_ptr_fn(self.value.data) };
131        let len = self.len();
132        Ok(PokeListIter {
133            data,
134            stride,
135            index: 0,
136            len,
137            elem_shape,
138            _list: PhantomData,
139        })
140    }
141
142    /// Push a value onto the end of the list.
143    ///
144    /// Returns an error if the underlying list type does not support push (e.g.
145    /// immutable lists) or if the value's shape does not match the list's
146    /// element type.
147    pub fn push<T: facet_core::Facet<'facet>>(&mut self, value: T) -> Result<(), ReflectError> {
148        if self.def.t() != T::SHAPE {
149            return Err(self.value.err(ReflectErrorKind::WrongShape {
150                expected: self.def.t(),
151                actual: T::SHAPE,
152            }));
153        }
154        let push_fn = self.push_fn()?;
155        let mut value = ManuallyDrop::new(value);
156        unsafe {
157            let item_ptr = PtrMut::new(&mut value as *mut ManuallyDrop<T> as *mut u8);
158            push_fn(self.value.data_mut(), item_ptr);
159        }
160        Ok(())
161    }
162
163    /// Type-erased [`push`](Self::push).
164    ///
165    /// Accepts a [`HeapValue`] whose shape must match the list's element type.
166    /// The value is moved out of the `HeapValue` into the list.
167    pub fn push_from_heap<const BORROW: bool>(
168        &mut self,
169        value: HeapValue<'facet, BORROW>,
170    ) -> Result<(), ReflectError> {
171        if self.def.t() != value.shape() {
172            return Err(self.value.err(ReflectErrorKind::WrongShape {
173                expected: self.def.t(),
174                actual: value.shape(),
175            }));
176        }
177        let push_fn = self.push_fn()?;
178        let mut value = value;
179        let guard = value
180            .guard
181            .take()
182            .expect("HeapValue guard was already taken");
183        unsafe {
184            let item_ptr = PtrMut::new(guard.ptr.as_ptr());
185            push_fn(self.value.data_mut(), item_ptr);
186        }
187        drop(guard);
188        Ok(())
189    }
190
191    /// Pop the last value off the end of the list.
192    ///
193    /// Returns `Ok(None)` if the list is empty. Returns an error if the
194    /// underlying list type does not support pop.
195    pub fn pop(&mut self) -> Result<Option<HeapValue<'facet, true>>, ReflectError> {
196        let pop_fn = self.def.pop().ok_or_else(|| {
197            self.value.err(ReflectErrorKind::OperationFailed {
198                shape: self.value.shape(),
199                operation: "pop: list type does not support pop",
200            })
201        })?;
202        let elem_shape = self.def.t();
203        let layout = elem_shape.layout.sized_layout().map_err(|_| {
204            self.value.err(ReflectErrorKind::Unsized {
205                shape: elem_shape,
206                operation: "pop",
207            })
208        })?;
209        let ptr = if layout.size() == 0 {
210            NonNull::<u8>::dangling()
211        } else {
212            let raw = unsafe { alloc::alloc::alloc(layout) };
213            match NonNull::new(raw) {
214                Some(p) => p,
215                None => alloc::alloc::handle_alloc_error(layout),
216            }
217        };
218        let out = PtrUninit::new(ptr.as_ptr());
219        let popped = unsafe { pop_fn(self.value.data_mut(), out) };
220        if !popped {
221            if layout.size() != 0 {
222                unsafe { alloc::alloc::dealloc(ptr.as_ptr(), layout) };
223            }
224            return Ok(None);
225        }
226        Ok(Some(HeapValue {
227            guard: Some(Guard {
228                ptr,
229                layout,
230                should_dealloc: layout.size() != 0,
231            }),
232            shape: elem_shape,
233            phantom: PhantomData,
234        }))
235    }
236
237    /// Swap the elements at indices `a` and `b`.
238    ///
239    /// Returns an error if the underlying list type does not support swap or
240    /// if either index is out of bounds. Swapping an index with itself is a
241    /// no-op.
242    pub fn swap(&mut self, a: usize, b: usize) -> Result<(), ReflectError> {
243        let swap_fn = self.def.vtable.swap.ok_or_else(|| {
244            self.value.err(ReflectErrorKind::OperationFailed {
245                shape: self.value.shape(),
246                operation: "swap: list type does not support swap",
247            })
248        })?;
249        let len = self.len();
250        let ok = unsafe { swap_fn(self.value.data_mut(), a, b, self.value.shape()) };
251        if !ok {
252            let out_of_bounds = if a >= len { a } else { b };
253            return Err(self.value.err(ReflectErrorKind::FieldError {
254                shape: self.value.shape(),
255                field_error: FieldError::IndexOutOfBounds {
256                    index: out_of_bounds,
257                    bound: len,
258                },
259            }));
260        }
261        Ok(())
262    }
263
264    /// Resolve the per-T push function or build an error if absent.
265    #[inline]
266    fn push_fn(&self) -> Result<facet_core::ListPushFn, ReflectError> {
267        self.def.push().ok_or_else(|| {
268            self.value.err(ReflectErrorKind::OperationFailed {
269                shape: self.value.shape(),
270                operation: "push: list type does not support push",
271            })
272        })
273    }
274
275    /// Def getter
276    #[inline]
277    pub const fn def(&self) -> ListDef {
278        self.def
279    }
280
281    /// Converts this `PokeList` back into a `Poke`
282    #[inline]
283    pub fn into_inner(self) -> Poke<'mem, 'facet> {
284        self.value
285    }
286
287    /// Returns a read-only `PeekList` view
288    #[inline]
289    pub fn as_peek_list(&self) -> crate::PeekList<'_, 'facet> {
290        unsafe { crate::PeekList::new(self.value.as_peek(), self.def) }
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use alloc::vec::Vec;
297
298    use super::*;
299
300    #[test]
301    fn poke_list_len() {
302        let mut v: Vec<i32> = alloc::vec![1, 2, 3, 4, 5];
303        let poke = Poke::new(&mut v);
304        let list = poke.into_list().unwrap();
305        assert_eq!(list.len(), 5);
306    }
307
308    #[test]
309    fn poke_list_get() {
310        let mut v: Vec<i32> = alloc::vec![10, 20, 30];
311        let poke = Poke::new(&mut v);
312        let list = poke.into_list().unwrap();
313
314        let item = list.get(1).unwrap();
315        assert_eq!(*item.get::<i32>().unwrap(), 20);
316    }
317
318    #[test]
319    fn poke_list_get_mut() {
320        let mut v: Vec<i32> = alloc::vec![10, 20, 30];
321        let poke = Poke::new(&mut v);
322        let mut list = poke.into_list().unwrap();
323
324        {
325            let mut item = list.get_mut(1).unwrap();
326            item.set(99i32).unwrap();
327        }
328
329        // Verify the change
330        let item = list.get(1).unwrap();
331        assert_eq!(*item.get::<i32>().unwrap(), 99);
332    }
333
334    #[test]
335    fn poke_list_iter_mut() {
336        let mut v: Vec<i32> = alloc::vec![1, 2, 3];
337        let poke = Poke::new(&mut v);
338        let list = poke.into_list().unwrap();
339
340        let mut sum = 0;
341        for mut item in list.iter_mut().unwrap() {
342            let val = *item.get::<i32>().unwrap();
343            item.set(val * 10).unwrap();
344            sum += val;
345        }
346
347        assert_eq!(sum, 6);
348        assert_eq!(v, alloc::vec![10, 20, 30]);
349    }
350
351    #[test]
352    fn poke_list_push_pop() {
353        let mut v: Vec<i32> = alloc::vec![];
354        {
355            let poke = Poke::new(&mut v);
356            let mut list = poke.into_list().unwrap();
357            list.push(1i32).unwrap();
358            list.push(2i32).unwrap();
359            list.push(3i32).unwrap();
360        }
361        assert_eq!(v, alloc::vec![1, 2, 3]);
362
363        {
364            let poke = Poke::new(&mut v);
365            let mut list = poke.into_list().unwrap();
366            let popped = list.pop().unwrap().unwrap();
367            assert_eq!(popped.materialize::<i32>().unwrap(), 3);
368        }
369        assert_eq!(v, alloc::vec![1, 2]);
370    }
371
372    #[test]
373    fn poke_list_pop_empty_returns_none() {
374        let mut v: Vec<i32> = alloc::vec![];
375        let poke = Poke::new(&mut v);
376        let mut list = poke.into_list().unwrap();
377        let popped = list.pop().unwrap();
378        assert!(popped.is_none());
379    }
380
381    #[test]
382    fn poke_list_push_wrong_shape_fails() {
383        let mut v: Vec<i32> = alloc::vec![];
384        let poke = Poke::new(&mut v);
385        let mut list = poke.into_list().unwrap();
386        let res = list.push(7u32);
387        assert!(matches!(
388            res,
389            Err(ref err) if matches!(err.kind, ReflectErrorKind::WrongShape { .. })
390        ));
391    }
392
393    #[test]
394    fn poke_list_push_from_heap() {
395        let mut v: Vec<i32> = alloc::vec![];
396        let poke = Poke::new(&mut v);
397        let mut list = poke.into_list().unwrap();
398
399        let hv = crate::Partial::alloc::<i32>()
400            .unwrap()
401            .set(42i32)
402            .unwrap()
403            .build()
404            .unwrap();
405        list.push_from_heap(hv).unwrap();
406        assert_eq!(v, alloc::vec![42]);
407    }
408
409    #[test]
410    fn poke_list_pop_string() {
411        let mut v: Vec<alloc::string::String> = alloc::vec![
412            alloc::string::String::from("a"),
413            alloc::string::String::from("b"),
414        ];
415        let poke = Poke::new(&mut v);
416        let mut list = poke.into_list().unwrap();
417        let popped = list.pop().unwrap().unwrap();
418        assert_eq!(popped.materialize::<alloc::string::String>().unwrap(), "b");
419        assert_eq!(v, alloc::vec![alloc::string::String::from("a")]);
420    }
421
422    #[test]
423    fn poke_list_swap() {
424        let mut v: Vec<i32> = alloc::vec![1, 2, 3];
425        let poke = Poke::new(&mut v);
426        let mut list = poke.into_list().unwrap();
427        list.swap(0, 2).unwrap();
428        assert_eq!(v, alloc::vec![3, 2, 1]);
429    }
430
431    #[test]
432    fn poke_list_swap_self_is_noop() {
433        let mut v: Vec<i32> = alloc::vec![1, 2, 3];
434        let poke = Poke::new(&mut v);
435        let mut list = poke.into_list().unwrap();
436        list.swap(1, 1).unwrap();
437        assert_eq!(v, alloc::vec![1, 2, 3]);
438    }
439
440    #[test]
441    fn poke_list_swap_out_of_bounds_fails() {
442        let mut v: Vec<i32> = alloc::vec![1, 2, 3];
443        let poke = Poke::new(&mut v);
444        let mut list = poke.into_list().unwrap();
445        let res = list.swap(0, 10);
446        assert!(matches!(
447            res,
448            Err(ref err) if matches!(err.kind, ReflectErrorKind::FieldError { .. })
449        ));
450    }
451}