Skip to main content

devela/data/layout/dst/
stack.rs

1// devela::data::layout::dst::stack
2//
3//! Implementation of the LIFO stack structure.
4//
5// TOC
6// - public API
7// - private API
8// - core_impls
9
10use super::{DstArray, DstBuf, check_fat_pointer, decompose_pointer, list_push_gen};
11use crate::{ConstInit, MaybeUninit, MemAligned, PhantomData, Ptr};
12
13/* public API */
14
15#[doc = crate::_tags!(data_structure)]
16/// A statically allocated LIFO stack of
17/// <abbr title="Dynamically sized type">DST</abbr>s with pointer alignment.
18#[doc = crate::_doc_meta!{location("data/layout/dst")}]
19///
20/// # Examples
21/// ```
22/// # use devela::data::DstStackUsize;
23/// let mut stack = DstStackUsize::<[u8], 16>::new();
24/// stack.push_copied(&[1]);
25/// ```
26// WAIT: [lazy_type_alias](https://github.com/rust-lang/rust/issues/112792) ↓DENIED
27pub type DstStackUsize<DST /*: ?Sized*/, const CAP: usize> = DstStack<DST, DstArray<usize, CAP>>;
28
29// Implementation Notes
30// -----
31//
32// The data array is filled from the back, with the metadata stored before
33// (at a lower memory address) the actual data. This so the code can use a
34// single integer to track the position (using size_of_val when popping items,
35// and the known size when pushing).
36
37#[doc = crate::_tags!(data_structure)]
38/// A statically allocated LIFO stack of <abbr title="Dynamically sized type">DST</abbr>s.
39#[doc = crate::_doc_meta!{location("data/layout/dst")}]
40///
41/// Note: Each item in the stack takes at least one slot in the buffer
42/// (to store the metadata)
43pub struct DstStack<DST: ?Sized, BUF: DstBuf> {
44    _pd: PhantomData<*const DST>,
45    // Offset from the _back_ of `data` to the next free position.
46    // I.e. data[data.len() - cur_ofs] is the first metadata word
47    next_ofs: usize,
48    data: BUF,
49}
50
51#[doc = crate::_tags!(iterator)]
52/// An iterator over the elements of a [`DstStack`].
53#[doc = crate::_doc_meta!{location("data/layout/dst")}]
54#[derive(Debug)]
55pub struct DstStackIter<'a, DST: 'a + ?Sized, BUF: 'a + DstBuf>(&'a DstStack<DST, BUF>, usize);
56
57#[doc = crate::_tags!(iterator)]
58/// A mutable iterator over the elements of a [`DstStack`].
59#[doc = crate::_doc_meta!{location("data/layout/dst")}]
60#[derive(Debug)]
61pub struct DstStackIterMut<'a, DST: 'a + ?Sized, BUF: 'a + DstBuf>(
62    &'a mut DstStack<DST, BUF>,
63    usize,
64);
65
66impl<DST: ?Sized, BUF: DstBuf> DstStack<DST, BUF> {
67    /// Constructs a new (empty) stack.
68    #[must_use] #[rustfmt::skip]
69    pub fn new() -> Self where BUF: Default { Self::with_buffer(BUF::default()) }
70
71    /// Constructs a new (empty) stack, in compile-time.
72    #[must_use] #[rustfmt::skip]
73    pub const fn new_const() -> Self where BUF: ConstInit { Self::with_buffer(BUF::INIT) }
74
75    /// Constructs a new (empty) stack using the given `buffer`.
76    #[must_use] #[rustfmt::skip]
77    pub const fn with_buffer(buffer: BUF) -> Self {
78        DstStack { _pd: PhantomData, next_ofs: 0, data: buffer }
79    }
80
81    /// Returns `true` if the stack is empty.
82    #[must_use]
83    pub const fn is_empty(&self) -> bool {
84        self.next_ofs == 0
85    }
86
87    /// Pushes a value at the top of the stack.
88    ///
89    /// ```
90    /// # use devela::data::{DstArray, DstStack};
91    /// let mut stack = DstStack::<[u8], DstArray<u64, 8>>::new();
92    /// stack.push([1, 2,3], |v| v);
93    /// ```
94    pub fn push<VAL, F: FnOnce(&VAL) -> &DST>(&mut self, v: VAL, f: F) -> Result<(), VAL>
95    where
96        (VAL, BUF::Inner): MemAligned,
97    {
98        <(VAL, BUF::Inner) as MemAligned>::assert_compatibility();
99
100        // SAFETY: Destination address is valid.
101        unsafe {
102            match self.push_inner(check_fat_pointer(&v, f)) {
103                Ok(pii) => {
104                    Ptr::write(pii.data.as_mut_ptr() as *mut VAL, v);
105                    Ok(())
106                }
107                Err(()) => Err(v),
108            }
109        }
110    }
111
112    /// Returns a shared reference to the top item on the stack.
113    #[must_use]
114    pub fn top(&self) -> Option<&DST> {
115        self.top_raw().map(|x| unsafe { &*x })
116    }
117
118    /// Returns an exclusive reference to the top item on the stack.
119    #[must_use]
120    pub fn top_mut(&mut self) -> Option<&mut DST> {
121        self.top_raw_mut().map(|x| unsafe { &mut *x })
122    }
123
124    /// Pops the top item off the stack.
125    pub fn pop(&mut self) {
126        if let Some(ptr) = self.top_raw_mut() {
127            assert!(self.next_ofs > 0);
128            // SAFETY: Pointer is valid, and will never be accessed after this point.
129            let words = unsafe {
130                let size = size_of_val(&*ptr);
131                Ptr::drop_in_place(ptr);
132                BUF::round_to_words(size)
133            };
134            self.next_ofs -= words + Self::meta_words();
135        }
136    }
137
138    /// Returns an immutable iterator
139    /// (yields references to items, in the order they would be popped).
140    ///
141    /// # Examples
142    /// ```
143    /// # use devela::data::{DstArray, DstStack};
144    /// let mut list = DstStack::<str, DstArray<usize, 8>>::new();
145    /// list.push_str("Hello");
146    /// list.push_str("world");
147    /// let mut it = list.iter();
148    /// assert_eq!(it.next(), Some("world"));
149    /// assert_eq!(it.next(), Some("Hello"));
150    /// assert_eq!(it.next(), None);
151    /// ```
152    #[must_use]
153    pub const fn iter(&self) -> DstStackIter<'_, DST, BUF> {
154        DstStackIter(self, self.next_ofs)
155    }
156
157    /// Returns a mutable iterator.
158    ///
159    /// # Examples
160    /// ```
161    /// # use devela::data::{DstArray, DstStack};
162    /// let mut list = DstStack::<[u8], DstArray<usize, 8>>::new();
163    /// list.push_copied(&[1,2,3]);
164    /// list.push_copied(&[9]);
165    /// for v in list.iter_mut() {
166    ///     v[0] -= 1;
167    /// }
168    /// let mut it = list.iter();
169    /// assert_eq!(it.next(), Some(&[8][..]));
170    /// assert_eq!(it.next(), Some(&[0,2,3][..]));
171    /// assert_eq!(it.next(), None);
172    /// ```
173    #[must_use]
174    pub fn iter_mut(&mut self) -> DstStackIterMut<'_, DST, BUF> {
175        DstStackIterMut(self, self.next_ofs)
176    }
177}
178
179impl<BUF: DstBuf> DstStack<str, BUF> {
180    /// Pushes the contents of a `string` slice as an item onto the stack.
181    ///
182    /// # Examples
183    /// ```
184    /// # use devela::data::{DstArray, DstStack};
185    /// let mut stack = DstStack::<str, DstArray<u8, 32>>::new();
186    /// stack.push_str("Hello!");
187    /// ```
188    pub fn push_str(&mut self, string: &str) -> Result<(), ()> {
189        unsafe {
190            self.push_inner(string).map(|pii| {
191                Ptr::copy(
192                    string.as_bytes().as_ptr(),
193                    pii.data.as_mut_ptr() as *mut u8,
194                    string.len(),
195                );
196            })
197        }
198    }
199}
200
201impl<BUF: DstBuf, DST: Clone> DstStack<[DST], BUF>
202where
203    (DST, BUF::Inner): MemAligned,
204{
205    /// Pushes a set of items (cloning out of the input `slice`).
206    ///
207    /// # Examples
208    /// ```
209    /// # use devela::data::{DstArray, DstStack};
210    /// let mut stack = DstStack::<[u8], DstArray<u64, 8>>::new();
211    /// stack.push_cloned(&[1, 2, 3]);
212    /// ```
213    pub fn push_cloned(&mut self, slice: &[DST]) -> Result<(), ()> {
214        <(DST, BUF::Inner) as MemAligned>::assert_compatibility();
215        self.push_from_iter(slice.iter().cloned())
216    }
217    /// Pushes a set of items (copying out of the input `slice`).
218    ///
219    /// # Examples
220    /// ```
221    /// # use devela::data::{DstArray, DstStack};
222    /// let mut stack = DstStack::<[u8], DstArray<u64, 8>>::new();
223    /// stack.push_copied(&[1, 2, 3]);
224    /// ```
225    pub fn push_copied(&mut self, slice: &[DST]) -> Result<(), ()>
226    where
227        DST: Copy,
228    {
229        <(DST, BUF::Inner) as MemAligned>::assert_compatibility();
230        // SAFETY: Carefully constructed to maintain consistency.
231        unsafe {
232            self.push_inner(slice).map(|pii| {
233                Ptr::copy(
234                    slice.as_ptr() as *const u8,
235                    pii.data.as_mut_ptr() as *mut u8,
236                    size_of_val(slice),
237                );
238            })
239        }
240    }
241}
242
243impl<BUF: DstBuf, DST> DstStack<[DST], BUF>
244where
245    (DST, BUF::Inner): MemAligned,
246{
247    /// Pushes an item, populated from an exact-sized iterator.
248    ///
249    /// # Examples
250    /// ```
251    /// # use devela::data::{DstArray, DstStack};
252    /// let mut stack = DstStack::<[u8], DstArray<usize, 8>>::new();
253    /// stack.push_from_iter(0..10);
254    /// assert_eq!(stack.top().unwrap(), &[0,1,2,3,4,5,6,7,8,9]);
255    /// ```
256    pub fn push_from_iter(
257        &mut self,
258        mut iter: impl ExactSizeIterator<Item = DST>,
259    ) -> Result<(), ()> {
260        <(DST, BUF::Inner) as MemAligned>::assert_compatibility();
261        // SAFETY: API used correctly.
262        unsafe {
263            let pii = self.push_inner_raw(iter.len() * size_of::<DST>(), &[0])?;
264            list_push_gen(
265                pii.meta,
266                pii.data,
267                iter.len(),
268                |_| iter.next().unwrap(),
269                pii.reset_slot,
270                pii.reset_value,
271            );
272            Ok(())
273        }
274    }
275}
276
277/* private API */
278
279struct PushInnerInfo<'a, DInner> {
280    // Buffer for value data
281    data: &'a mut [MaybeUninit<DInner>],
282
283    // Buffer for metadata (length/vtable)
284    meta: &'a mut [MaybeUninit<DInner>],
285
286    // Memory location for resetting the push
287    reset_slot: &'a mut usize,
288    reset_value: usize,
289}
290
291impl<DST: ?Sized, BUF: DstBuf> DstStack<DST, BUF> {
292    #[must_use]
293    fn meta_words() -> usize {
294        BUF::round_to_words(size_of::<&DST>() - size_of::<usize>())
295    }
296
297    #[must_use]
298    unsafe fn raw_at(&self, ofs: usize) -> *mut DST {
299        let dar = self.data.as_ref();
300        let meta = &dar[dar.len() - ofs..];
301        let mw = Self::meta_words();
302        let (meta, data) = meta.split_at(mw);
303        // SAFETY: caller must ensure safety
304        unsafe { super::make_fat_ptr(data.as_ptr() as *mut (), meta) }
305    }
306    #[must_use]
307    unsafe fn raw_at_mut(&mut self, ofs: usize) -> *mut DST {
308        let dar = self.data.as_mut();
309        let ofs = dar.len() - ofs;
310        let meta = &mut dar[ofs..];
311        let mw = Self::meta_words();
312        let (meta, data) = meta.split_at_mut(mw);
313        // SAFETY: caller must ensure safety
314        unsafe { super::make_fat_ptr(data.as_mut_ptr() as *mut (), meta) }
315    }
316    // Get a raw pointer to the top of the stack.
317    #[must_use]
318    fn top_raw(&self) -> Option<*mut DST> {
319        if self.next_ofs == 0 {
320            None
321        } else {
322            // SAFETY: Internal consistency maintains the metadata validity.
323            Some(unsafe { self.raw_at(self.next_ofs) })
324        }
325    }
326    // Get a raw pointer to the top of the stack
327    #[must_use]
328    fn top_raw_mut(&mut self) -> Option<*mut DST> {
329        if self.next_ofs == 0 {
330            None
331        } else {
332            // SAFETY: Internal consistency maintains the metadata validity.
333            Some(unsafe { self.raw_at_mut(self.next_ofs) })
334        }
335    }
336
337    // See `push_inner_raw`.
338    unsafe fn push_inner(&mut self, fat_ptr: &DST) -> Result<PushInnerInfo<'_, BUF::Inner>, ()> {
339        let bytes = size_of_val(fat_ptr);
340        let (_data_ptr, len, v) = decompose_pointer(fat_ptr);
341        // SAFETY: caller must ensure safety
342        unsafe { self.push_inner_raw(bytes, &v[..len]) }
343    }
344
345    // Returns:
346    // - metadata slot
347    // - data slot
348    // - Total words used
349    unsafe fn push_inner_raw(
350        &mut self,
351        bytes: usize,
352        metadata: &[usize],
353    ) -> Result<PushInnerInfo<'_, BUF::Inner>, ()> {
354        assert!(BUF::round_to_words(size_of_val(metadata)) == Self::meta_words());
355        let words = BUF::round_to_words(bytes) + Self::meta_words();
356
357        let req_space = self.next_ofs + words;
358        // Attempt to resize (if the underlying buffer allows it).
359        if req_space > self.data.as_ref().len() {
360            let old_len = self.data.as_ref().len();
361            if self.data.extend(req_space).is_ok() {
362                let new_len = self.data.as_ref().len();
363                self.data.as_mut().rotate_right(new_len - old_len);
364            }
365        }
366
367        // Check if there is sufficient space for the new item.
368        if req_space <= self.data.as_ref().len() {
369            // Get the base pointer for the new item
370            let prev_next_ofs = self.next_ofs;
371            self.next_ofs += words;
372            let len = self.data.as_ref().len();
373            let slot = &mut self.data.as_mut()[len - self.next_ofs..][..words];
374            let (meta, rv) = slot.split_at_mut(Self::meta_words());
375
376            // Populate the metadata.
377            super::store_metadata(meta, metadata);
378
379            // Increment offset and return.
380            Ok(PushInnerInfo {
381                meta,
382                data: rv,
383                reset_slot: &mut self.next_ofs,
384                reset_value: prev_next_ofs,
385            })
386        } else {
387            Err(())
388        }
389    }
390}
391
392mod core_impls {
393    use super::{DstBuf, DstStack, DstStackIter, DstStackIterMut};
394    use core::{fmt, iter, ops};
395
396    impl<DST: ?Sized, BUF: DstBuf> ops::Drop for DstStack<DST, BUF> {
397        fn drop(&mut self) {
398            while !self.is_empty() {
399                self.pop();
400            }
401        }
402    }
403    impl<DST: ?Sized, BUF: DstBuf + Default> Default for DstStack<DST, BUF> {
404        fn default() -> Self {
405            DstStack::new()
406        }
407    }
408
409    macro_rules! impl_trait {
410        ( $t:path; $($body:tt)* ) => {
411            impl<BUF: DstBuf, DST: ?Sized> $t for DstStack<DST, BUF> where DST: $t { $( $body )* }
412        }
413    }
414    // FIXME
415    impl_trait! { fmt::Debug;
416        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417            f.write_str("[")?;
418            for v in self.iter() {
419                v.fmt(f)?;
420                f.write_str(",")?;
421            }
422            f.write_str("]")?;
423            Ok( () )
424        }
425    }
426
427    /* iter */
428
429    impl<'a, DST: 'a + ?Sized, BUF: 'a + DstBuf> iter::Iterator for DstStackIter<'a, DST, BUF> {
430        type Item = &'a DST;
431        fn next(&mut self) -> Option<&'a DST> {
432            if self.1 == 0 {
433                None
434            } else {
435                // SAFETY: Bounds checked, aliasing enforced by API.
436                let rv = unsafe { &*self.0.raw_at(self.1) };
437                self.1 -= DstStack::<DST, BUF>::meta_words() + BUF::round_to_words(size_of_val(rv));
438                Some(rv)
439            }
440        }
441    }
442
443    impl<'a, DST: 'a + ?Sized, BUF: 'a + DstBuf> iter::Iterator for DstStackIterMut<'a, DST, BUF> {
444        type Item = &'a mut DST;
445        fn next(&mut self) -> Option<&'a mut DST> {
446            if self.1 == 0 {
447                None
448            } else {
449                // SAFETY: Bounds checked, aliasing enforced by API.
450                let rv = unsafe { &mut *self.0.raw_at_mut(self.1) };
451                self.1 -= DstStack::<DST, BUF>::meta_words() + BUF::round_to_words(size_of_val(rv));
452                Some(rv)
453            }
454        }
455    }
456}