bbx_core/
stack_vec.rs

1//! Stack-allocated vector with compile-time capacity.
2//!
3//! Provides a fixed-capacity vector that lives entirely on the stack,
4//! suitable for use in realtime audio processing where heap allocations
5//! must be avoided.
6
7use core::{
8    mem::MaybeUninit,
9    ops::{Index, IndexMut},
10    ptr,
11};
12
13/// A stack-allocated vector with compile-time capacity `N`.
14///
15/// This type provides `Vec`-like functionality without heap allocation,
16/// making it suitable for use in audio processing hot paths where
17/// allocation latency is unacceptable.
18///
19/// # Examples
20///
21/// ```
22/// use bbx_core::StackVec;
23///
24/// let mut vec: StackVec<i32, 4> = StackVec::new();
25/// vec.push(1).unwrap();
26/// vec.push(2).unwrap();
27///
28/// assert_eq!(vec.len(), 2);
29/// assert_eq!(vec[0], 1);
30/// ```
31pub struct StackVec<T, const N: usize> {
32    data: [MaybeUninit<T>; N],
33    len: usize,
34}
35
36impl<T, const N: usize> StackVec<T, N> {
37    /// Creates a new empty `StackVec`.
38    ///
39    /// This is a const fn and can be used in const contexts.
40    #[inline]
41    pub const fn new() -> Self {
42        Self {
43            // SAFETY: An array of MaybeUninit<T> doesn't require initialization
44            data: unsafe { MaybeUninit::uninit().assume_init() },
45            len: 0,
46        }
47    }
48
49    /// Pushes a value onto the end of the vector.
50    ///
51    /// Returns `Ok(())` if successful, or `Err(value)` if the vector is full.
52    #[inline]
53    pub fn push(&mut self, value: T) -> Result<(), T> {
54        if self.len >= N {
55            return Err(value);
56        }
57        // SAFETY: We've verified len < N, so this index is valid
58        unsafe {
59            self.data.get_unchecked_mut(self.len).write(value);
60        }
61        self.len += 1;
62        Ok(())
63    }
64
65    /// Pushes a value onto the end of the vector without bounds checking.
66    ///
67    /// # Panics
68    ///
69    /// Panics in debug mode if the vector is full.
70    /// In release mode, this will not panic but the behavior is safe
71    /// due to the debug assertion.
72    #[inline]
73    pub fn push_unchecked(&mut self, value: T) {
74        debug_assert!(self.len < N, "StackVec capacity exceeded");
75        // SAFETY: Debug assertion ensures len < N in debug builds.
76        // In release, we still check to prevent UB.
77        if self.len < N {
78            unsafe {
79                self.data.get_unchecked_mut(self.len).write(value);
80            }
81            self.len += 1;
82        }
83    }
84
85    /// Removes and returns the last element, or `None` if empty.
86    #[inline]
87    pub fn pop(&mut self) -> Option<T> {
88        if self.len == 0 {
89            return None;
90        }
91        self.len -= 1;
92        // SAFETY: The element at len was initialized
93        unsafe { Some(self.data.get_unchecked(self.len).assume_init_read()) }
94    }
95
96    /// Returns the number of elements in the vector.
97    #[inline]
98    pub const fn len(&self) -> usize {
99        self.len
100    }
101
102    /// Returns `true` if the vector contains no elements.
103    #[inline]
104    pub const fn is_empty(&self) -> bool {
105        self.len == 0
106    }
107
108    /// Returns `true` if the vector is at capacity.
109    #[inline]
110    pub const fn is_full(&self) -> bool {
111        self.len == N
112    }
113
114    /// Returns the maximum capacity of the vector.
115    #[inline]
116    pub const fn capacity(&self) -> usize {
117        N
118    }
119
120    /// Removes all elements from the vector.
121    #[inline]
122    pub fn clear(&mut self) {
123        while self.pop().is_some() {}
124    }
125
126    /// Returns a slice of the initialized elements.
127    #[inline]
128    pub fn as_slice(&self) -> &[T] {
129        // SAFETY: Elements 0..len are initialized
130        unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const T, self.len) }
131    }
132
133    /// Returns a mutable slice of the initialized elements.
134    #[inline]
135    pub fn as_mut_slice(&mut self) -> &mut [T] {
136        // SAFETY: Elements 0..len are initialized
137        unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut T, self.len) }
138    }
139
140    /// Returns a reference to the element at the given index, or `None` if out of bounds.
141    #[inline]
142    pub fn get(&self, index: usize) -> Option<&T> {
143        if index < self.len {
144            // SAFETY: We've verified index < len, so element is initialized
145            unsafe { Some(&*self.data.get_unchecked(index).as_ptr()) }
146        } else {
147            None
148        }
149    }
150
151    /// Returns a mutable reference to the element at the given index, or `None` if out of bounds.
152    #[inline]
153    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
154        if index < self.len {
155            // SAFETY: We've verified index < len, so element is initialized
156            unsafe { Some(&mut *self.data.get_unchecked_mut(index).as_mut_ptr()) }
157        } else {
158            None
159        }
160    }
161}
162
163impl<T, const N: usize> Default for StackVec<T, N> {
164    #[inline]
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl<T: core::fmt::Debug, const N: usize> core::fmt::Debug for StackVec<T, N> {
171    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172        f.debug_list().entries(self.as_slice()).finish()
173    }
174}
175
176impl<T, const N: usize> Drop for StackVec<T, N> {
177    fn drop(&mut self) {
178        for i in 0..self.len {
179            // SAFETY: Elements 0..len are initialized
180            unsafe {
181                ptr::drop_in_place(self.data[i].as_mut_ptr());
182            }
183        }
184    }
185}
186
187impl<T, const N: usize> Index<usize> for StackVec<T, N> {
188    type Output = T;
189
190    #[inline]
191    fn index(&self, index: usize) -> &Self::Output {
192        assert!(index < self.len, "index out of bounds");
193        // SAFETY: We've verified index < len
194        unsafe { &*self.data.get_unchecked(index).as_ptr() }
195    }
196}
197
198impl<T, const N: usize> IndexMut<usize> for StackVec<T, N> {
199    #[inline]
200    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
201        assert!(index < self.len, "index out of bounds");
202        // SAFETY: We've verified index < len
203        unsafe { &mut *self.data.get_unchecked_mut(index).as_mut_ptr() }
204    }
205}
206
207impl<T: Clone, const N: usize> Clone for StackVec<T, N> {
208    fn clone(&self) -> Self {
209        let mut new_vec = Self::new();
210        for item in self.as_slice() {
211            // This won't fail since we're cloning from a vec with same capacity
212            let _ = new_vec.push(item.clone());
213        }
214        new_vec
215    }
216}
217
218// Note: Copy cannot be implemented for StackVec because it has a custom Drop.
219// For Copy types, use StackVec::clone() which is efficient due to T: Copy.
220
221// Iterator for owned values
222pub struct StackVecIntoIter<T, const N: usize> {
223    vec: StackVec<T, N>,
224    index: usize,
225}
226
227impl<T, const N: usize> Iterator for StackVecIntoIter<T, N> {
228    type Item = T;
229
230    #[inline]
231    fn next(&mut self) -> Option<Self::Item> {
232        if self.index < self.vec.len {
233            // SAFETY: index < len means element is initialized
234            let value = unsafe { self.vec.data.get_unchecked(self.index).assume_init_read() };
235            self.index += 1;
236            Some(value)
237        } else {
238            None
239        }
240    }
241
242    #[inline]
243    fn size_hint(&self) -> (usize, Option<usize>) {
244        let remaining = self.vec.len - self.index;
245        (remaining, Some(remaining))
246    }
247}
248
249impl<T, const N: usize> ExactSizeIterator for StackVecIntoIter<T, N> {}
250
251impl<T, const N: usize> Drop for StackVecIntoIter<T, N> {
252    fn drop(&mut self) {
253        for i in self.index..self.vec.len {
254            // SAFETY: Elements index..len are still initialized
255            unsafe {
256                ptr::drop_in_place(self.vec.data[i].as_mut_ptr());
257            }
258        }
259        // Prevent StackVec's Drop from running on already-handled elements
260        self.vec.len = 0;
261    }
262}
263
264impl<T, const N: usize> IntoIterator for StackVec<T, N> {
265    type Item = T;
266    type IntoIter = StackVecIntoIter<T, N>;
267
268    #[inline]
269    fn into_iter(self) -> Self::IntoIter {
270        StackVecIntoIter { vec: self, index: 0 }
271    }
272}
273
274impl<'a, T, const N: usize> IntoIterator for &'a StackVec<T, N> {
275    type Item = &'a T;
276    type IntoIter = core::slice::Iter<'a, T>;
277
278    #[inline]
279    fn into_iter(self) -> Self::IntoIter {
280        self.as_slice().iter()
281    }
282}
283
284impl<'a, T, const N: usize> IntoIterator for &'a mut StackVec<T, N> {
285    type Item = &'a mut T;
286    type IntoIter = core::slice::IterMut<'a, T>;
287
288    #[inline]
289    fn into_iter(self) -> Self::IntoIter {
290        self.as_mut_slice().iter_mut()
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use std::rc::Rc;
297
298    use super::*;
299
300    #[test]
301    fn test_new_and_empty() {
302        let vec: StackVec<i32, 4> = StackVec::new();
303        assert!(vec.is_empty());
304        assert_eq!(vec.len(), 0);
305        assert_eq!(vec.capacity(), 4);
306        assert!(!vec.is_full());
307    }
308
309    #[test]
310    fn test_push_and_pop() {
311        let mut vec: StackVec<i32, 4> = StackVec::new();
312
313        assert!(vec.push(1).is_ok());
314        assert!(vec.push(2).is_ok());
315        assert!(vec.push(3).is_ok());
316        assert_eq!(vec.len(), 3);
317
318        assert_eq!(vec.pop(), Some(3));
319        assert_eq!(vec.pop(), Some(2));
320        assert_eq!(vec.pop(), Some(1));
321        assert_eq!(vec.pop(), None);
322        assert!(vec.is_empty());
323    }
324
325    #[test]
326    fn test_capacity_limit() {
327        let mut vec: StackVec<i32, 2> = StackVec::new();
328
329        assert!(vec.push(1).is_ok());
330        assert!(vec.push(2).is_ok());
331        assert!(vec.is_full());
332
333        // Should fail and return the value
334        let result = vec.push(3);
335        assert_eq!(result, Err(3));
336        assert_eq!(vec.len(), 2);
337    }
338
339    #[test]
340    fn test_push_unchecked() {
341        let mut vec: StackVec<i32, 4> = StackVec::new();
342        vec.push_unchecked(1);
343        vec.push_unchecked(2);
344        assert_eq!(vec.len(), 2);
345        assert_eq!(vec[0], 1);
346        assert_eq!(vec[1], 2);
347    }
348
349    #[test]
350    #[cfg(debug_assertions)]
351    #[should_panic(expected = "StackVec capacity exceeded")]
352    fn test_push_unchecked_overflow_panics_in_debug() {
353        let mut vec: StackVec<i32, 1> = StackVec::new();
354        vec.push_unchecked(1);
355        vec.push_unchecked(2); // Should panic in debug mode
356    }
357
358    #[test]
359    fn test_indexing() {
360        let mut vec: StackVec<i32, 4> = StackVec::new();
361        vec.push(10).unwrap();
362        vec.push(20).unwrap();
363
364        assert_eq!(vec[0], 10);
365        assert_eq!(vec[1], 20);
366
367        vec[0] = 100;
368        assert_eq!(vec[0], 100);
369    }
370
371    #[test]
372    #[should_panic(expected = "index out of bounds")]
373    fn test_index_out_of_bounds() {
374        let vec: StackVec<i32, 4> = StackVec::new();
375        let _ = vec[0];
376    }
377
378    #[test]
379    fn test_get() {
380        let mut vec: StackVec<i32, 4> = StackVec::new();
381        vec.push(1).unwrap();
382
383        assert_eq!(vec.get(0), Some(&1));
384        assert_eq!(vec.get(1), None);
385    }
386
387    #[test]
388    fn test_get_mut() {
389        let mut vec: StackVec<i32, 4> = StackVec::new();
390        vec.push(1).unwrap();
391
392        if let Some(val) = vec.get_mut(0) {
393            *val = 42;
394        }
395        assert_eq!(vec[0], 42);
396    }
397
398    #[test]
399    fn test_clear() {
400        let mut vec: StackVec<i32, 4> = StackVec::new();
401        vec.push(1).unwrap();
402        vec.push(2).unwrap();
403        vec.push(3).unwrap();
404
405        vec.clear();
406        assert!(vec.is_empty());
407        assert_eq!(vec.len(), 0);
408
409        // Can reuse after clear
410        vec.push(42).unwrap();
411        assert_eq!(vec[0], 42);
412    }
413
414    #[test]
415    fn test_as_slice() {
416        let mut vec: StackVec<i32, 4> = StackVec::new();
417        vec.push(1).unwrap();
418        vec.push(2).unwrap();
419        vec.push(3).unwrap();
420
421        assert_eq!(vec.as_slice(), &[1, 2, 3]);
422    }
423
424    #[test]
425    fn test_as_mut_slice() {
426        let mut vec: StackVec<i32, 4> = StackVec::new();
427        vec.push(1).unwrap();
428        vec.push(2).unwrap();
429
430        vec.as_mut_slice()[0] = 10;
431        assert_eq!(vec[0], 10);
432    }
433
434    #[test]
435    fn test_iter_ref() {
436        let mut vec: StackVec<i32, 4> = StackVec::new();
437        vec.push(1).unwrap();
438        vec.push(2).unwrap();
439        vec.push(3).unwrap();
440
441        let collected: Vec<_> = (&vec).into_iter().copied().collect();
442        assert_eq!(collected, vec![1, 2, 3]);
443    }
444
445    #[test]
446    fn test_iter_mut() {
447        let mut vec: StackVec<i32, 4> = StackVec::new();
448        vec.push(1).unwrap();
449        vec.push(2).unwrap();
450
451        for val in &mut vec {
452            *val *= 2;
453        }
454
455        assert_eq!(vec[0], 2);
456        assert_eq!(vec[1], 4);
457    }
458
459    #[test]
460    fn test_into_iter() {
461        let mut vec: StackVec<i32, 4> = StackVec::new();
462        vec.push(1).unwrap();
463        vec.push(2).unwrap();
464        vec.push(3).unwrap();
465
466        let collected: Vec<_> = vec.into_iter().collect();
467        assert_eq!(collected, vec![1, 2, 3]);
468    }
469
470    #[test]
471    fn test_clone() {
472        let mut vec: StackVec<i32, 4> = StackVec::new();
473        vec.push(1).unwrap();
474        vec.push(2).unwrap();
475
476        let cloned = vec.clone();
477        assert_eq!(cloned.len(), 2);
478        assert_eq!(cloned[0], 1);
479        assert_eq!(cloned[1], 2);
480    }
481
482    #[test]
483    fn test_drop_semantics_with_non_copy() {
484        // Use Rc to track drop calls
485        let counter = Rc::new(());
486
487        {
488            let mut vec: StackVec<Rc<()>, 4> = StackVec::new();
489            vec.push(Rc::clone(&counter)).unwrap();
490            vec.push(Rc::clone(&counter)).unwrap();
491            vec.push(Rc::clone(&counter)).unwrap();
492
493            // counter + 3 clones = 4 refs
494            assert_eq!(Rc::strong_count(&counter), 4);
495        }
496
497        // After vec drops, only original counter remains
498        assert_eq!(Rc::strong_count(&counter), 1);
499    }
500
501    #[test]
502    fn test_drop_on_clear_with_non_copy() {
503        let counter = Rc::new(());
504
505        let mut vec: StackVec<Rc<()>, 4> = StackVec::new();
506        vec.push(Rc::clone(&counter)).unwrap();
507        vec.push(Rc::clone(&counter)).unwrap();
508
509        assert_eq!(Rc::strong_count(&counter), 3);
510
511        vec.clear();
512        assert_eq!(Rc::strong_count(&counter), 1);
513    }
514
515    #[test]
516    fn test_into_iter_drop_remaining() {
517        let counter = Rc::new(());
518
519        let mut vec: StackVec<Rc<()>, 4> = StackVec::new();
520        vec.push(Rc::clone(&counter)).unwrap();
521        vec.push(Rc::clone(&counter)).unwrap();
522        vec.push(Rc::clone(&counter)).unwrap();
523
524        assert_eq!(Rc::strong_count(&counter), 4);
525
526        // Only consume first element
527        let mut iter = vec.into_iter();
528        drop(iter.next()); // Consume and immediately drop first element
529
530        // Drop iter without consuming all elements
531        drop(iter);
532
533        // All elements should be dropped
534        assert_eq!(Rc::strong_count(&counter), 1);
535    }
536
537    #[test]
538    fn test_default() {
539        let vec: StackVec<i32, 4> = StackVec::default();
540        assert!(vec.is_empty());
541    }
542
543    #[test]
544    fn test_size_hint() {
545        let mut vec: StackVec<i32, 4> = StackVec::new();
546        vec.push(1).unwrap();
547        vec.push(2).unwrap();
548        vec.push(3).unwrap();
549
550        let iter = vec.into_iter();
551        assert_eq!(iter.size_hint(), (3, Some(3)));
552        assert_eq!(iter.len(), 3);
553    }
554
555    #[test]
556    fn test_zero_capacity() {
557        let vec: StackVec<i32, 0> = StackVec::new();
558        assert!(vec.is_empty());
559        assert!(vec.is_full());
560        assert_eq!(vec.capacity(), 0);
561    }
562}