tl/inline/
vec.rs

1use std::fmt::{Debug, Formatter};
2use std::mem::MaybeUninit;
3use std::ops::Index;
4use std::ptr;
5
6/// A wrapper around a `Vec<T>` that lives on the stack if it is small enough.
7#[derive(Debug, Clone)]
8pub struct InlineVec<T, const N: usize>(InlineVecInner<T, N>);
9
10impl<T, const N: usize> InlineVec<T, N> {
11    /// Creates a new InlineVec
12    #[inline]
13    pub(crate) fn new() -> Self {
14        Self(InlineVecInner::new())
15    }
16
17    /// Returns the number of elements in the vector
18    #[inline]
19    pub fn len(&self) -> usize {
20        self.0.len()
21    }
22
23    /// Returns true if the vector contains no elements
24    #[inline]
25    pub fn is_empty(&self) -> bool {
26        self.len() == 0
27    }
28
29    /// Checks whether this vector is allocated on the heap
30    #[inline]
31    pub fn is_heap_allocated(&self) -> bool {
32        self.0.is_heap_allocated()
33    }
34
35    /// If `self` is inlined, this returns the underlying raw parts that make up this `InlineVec`.
36    ///
37    /// Only the first `.1` elements are initialized.
38    #[inline]
39    pub fn inline_parts_mut(&mut self) -> Option<(&mut [MaybeUninit<T>; N], usize)> {
40        self.0.inline_parts_mut()
41    }
42
43    /// Copies `self` into a new `Vec<T>`
44    #[inline]
45    pub fn to_vec(&self) -> Vec<T>
46    where
47        T: Clone,
48    {
49        self.0.to_vec()
50    }
51
52    /// Inserts a new element into the vector
53    #[inline]
54    pub fn push(&mut self, value: T) {
55        self.0.push(value)
56    }
57
58    /// Returns a reference to the value at the given index
59    #[inline]
60    pub fn get(&self, index: usize) -> Option<&T> {
61        self.0.get(index)
62    }
63
64    /// Returns a mutable reference to the value at the given index
65    #[inline]
66    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
67        self.0.get_mut(index)
68    }
69
70    /// Removes an element at a given index
71    ///
72    /// # Panics
73    /// Just like `Vec::remove`, this method will panic if the index is out of bounds.
74    #[inline]
75    pub fn remove(&mut self, index: usize) -> T {
76        self.0.remove(index)
77    }
78
79    /// Returns an iterator over the elements of this vector
80    #[inline]
81    pub fn iter(&self) -> InlineVecIter<'_, T, N> {
82        self.0.iter()
83    }
84
85    /// Returns a slice to the contents of this vector
86    #[inline]
87    pub fn as_slice(&self) -> &[T] {
88        self.0.as_slice()
89    }
90}
91
92enum InlineVecInner<T, const N: usize> {
93    Inline {
94        len: usize,
95        data: [MaybeUninit<T>; N],
96    },
97    Heap(Vec<T>),
98}
99
100impl<T, const N: usize> Debug for InlineVecInner<T, N>
101where
102    T: Debug,
103{
104    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
105        write!(f, "InlineVec<{} items>", self.len())
106    }
107}
108
109impl<T, const N: usize> Clone for InlineVecInner<T, N>
110where
111    T: Clone,
112{
113    fn clone(&self) -> Self {
114        match self {
115            Self::Heap(m) => Self::Heap(m.clone()),
116            Self::Inline { len, data } => {
117                let mut new_data = super::uninit_array();
118
119                let iter = data.iter().take(*len).enumerate();
120
121                for (idx, element) in iter {
122                    let element = unsafe { &*element.as_ptr() };
123                    new_data[idx] = MaybeUninit::new(T::clone(element));
124                }
125
126                Self::Inline {
127                    len: *len,
128                    data: new_data,
129                }
130            }
131        }
132    }
133}
134
135impl<T, const N: usize> InlineVecInner<T, N> {
136    #[inline]
137    pub(crate) fn new() -> Self {
138        Self::Inline {
139            len: 0,
140            data: super::uninit_array(),
141        }
142    }
143
144    pub fn as_slice(&self) -> &[T] {
145        match self {
146            Self::Heap(v) => v.as_slice(),
147            Self::Inline { len, data } => unsafe {
148                std::slice::from_raw_parts(data.as_ptr() as *const T, *len)
149            },
150        }
151    }
152
153    #[inline]
154    pub fn inline_parts_mut(&mut self) -> Option<(&mut [MaybeUninit<T>; N], usize)> {
155        match self {
156            Self::Heap(_) => None,
157            Self::Inline { len, data } => Some((data, *len)),
158        }
159    }
160
161    pub fn to_vec(&self) -> Vec<T>
162    where
163        T: Clone,
164    {
165        match &self {
166            InlineVecInner::Heap(m) => m.to_vec(),
167            InlineVecInner::Inline { len, data } => {
168                let mut new_data = Vec::with_capacity(*len);
169
170                let iter = data.iter().take(*len);
171
172                for element in iter {
173                    new_data.push(unsafe { T::clone(&*element.as_ptr()) });
174                }
175
176                new_data
177            }
178        }
179    }
180
181    #[inline]
182    pub fn iter(&self) -> InlineVecIter<'_, T, N> {
183        InlineVecIter { idx: 0, vec: self }
184    }
185
186    #[inline]
187    pub fn len(&self) -> usize {
188        match self {
189            Self::Inline { len, .. } => *len,
190            Self::Heap(vec) => vec.len(),
191        }
192    }
193
194    pub fn get(&self, idx: usize) -> Option<&T> {
195        match self {
196            Self::Inline { data, len } => {
197                if idx < *len {
198                    Some(unsafe { &*data.get_unchecked(idx).as_ptr() })
199                } else {
200                    None
201                }
202            }
203            Self::Heap(vec) => vec.get(idx),
204        }
205    }
206
207    pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
208        match self {
209            Self::Inline { data, len } => {
210                if idx < *len {
211                    Some(unsafe { &mut *data.get_unchecked_mut(idx).as_mut_ptr() })
212                } else {
213                    None
214                }
215            }
216            Self::Heap(vec) => vec.get_mut(idx),
217        }
218    }
219
220    pub fn remove(&mut self, idx: usize) -> T {
221        match self {
222            Self::Inline { data, len } => {
223                assert!(idx < *len);
224
225                // at this point we know idx is in bounds
226                // carefully replace the value with MaybeUninit::uninit(), so it can be returned
227                let element = unsafe {
228                    std::mem::replace(data.get_unchecked_mut(idx), MaybeUninit::uninit())
229                };
230
231                for i in idx + 1..*len {
232                    // TODO(y21): data.swap_unchecked() worth it?
233                    data.swap(i, i - 1);
234                }
235
236                *len -= 1;
237
238                // we've made sure that idx is in bounds and if idx is in bounds, then `T` must be initialized
239                unsafe { element.assume_init() }
240            }
241            Self::Heap(h) => h.remove(idx),
242        }
243    }
244
245    pub fn push(&mut self, value: T) {
246        let (array, len) = match self {
247            Self::Inline { data, len } => (data, len),
248            Self::Heap(vec) => {
249                vec.push(value);
250                return;
251            }
252        };
253
254        if *len >= N {
255            let mut vec = Vec::with_capacity(*len + 1);
256
257            // move old elements to heap
258            for element in array.iter_mut().take(*len) {
259                let element = std::mem::replace(element, MaybeUninit::uninit());
260
261                vec.push(unsafe { element.assume_init() });
262            }
263
264            // push the new element
265            vec.push(value);
266            let new_heap = InlineVecInner::Heap(vec);
267
268            // do not call the destructor!
269            unsafe { ptr::write(self, new_heap) };
270        } else {
271            array[*len].write(value);
272            *len += 1;
273        }
274    }
275
276    #[inline]
277    pub fn is_heap_allocated(&self) -> bool {
278        matches!(self, Self::Heap(_))
279    }
280}
281
282impl<T, const N: usize> Index<usize> for InlineVec<T, N> {
283    type Output = T;
284
285    fn index(&self, idx: usize) -> &Self::Output {
286        self.0.get(idx).expect("index out of bounds")
287    }
288}
289
290/// An iterator over the elements stored in an [`InlineVec`]
291pub struct InlineVecIter<'a, T, const N: usize> {
292    vec: &'a InlineVecInner<T, N>,
293    idx: usize,
294}
295
296impl<'a, T, const N: usize> Iterator for InlineVecIter<'a, T, N> {
297    type Item = &'a T;
298
299    fn next(&mut self) -> Option<Self::Item> {
300        self.idx += 1;
301        self.vec.get(self.idx - 1)
302    }
303}
304
305impl<T, const N: usize> Drop for InlineVecInner<T, N> {
306    fn drop(&mut self) {
307        if let Some((data, len)) = self.inline_parts_mut() {
308            for element in data.iter_mut().take(len) {
309                unsafe { ptr::drop_in_place(element.as_mut_ptr()) };
310            }
311        }
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    #[test]
320    fn inlinevec_to_vec_stack() {
321        let mut x = InlineVec::<usize, 4>::new();
322
323        for i in 0..4 {
324            x.push(i * 2);
325        }
326
327        assert!(!x.is_heap_allocated());
328        assert_eq!(x.len(), 4);
329
330        let xx = x.to_vec();
331        assert_eq!(xx.as_slice(), &[0, 2, 4, 6]);
332
333        x.push(42);
334        assert!(x.is_heap_allocated());
335        assert_eq!(x.as_slice(), &[0, 2, 4, 6, 42]);
336        assert_eq!(x.get(4), Some(&42));
337
338        let xx = x.to_vec();
339        assert_eq!(xx.as_slice(), &[0, 2, 4, 6, 42]);
340    }
341
342    #[test]
343    fn inlinevec_to_vec_heap() {
344        let mut x = InlineVec::<String, 4>::new();
345
346        for i in 0..4u8 {
347            x.push(i.to_string());
348        }
349
350        assert!(!x.is_heap_allocated());
351        assert_eq!(x.len(), 4);
352
353        let xx = x.to_vec();
354        assert_eq!(xx.as_slice(), &["0", "1", "2", "3"]);
355
356        x.push("1337".into());
357        assert!(x.is_heap_allocated());
358        assert_eq!(x.as_slice(), &["0", "1", "2", "3", "1337"]);
359        assert_eq!(x.get(4).map(|x| &**x), Some("1337"));
360
361        let xx = x.to_vec();
362        assert_eq!(xx.as_slice(), &["0", "1", "2", "3", "1337"]);
363    }
364
365    #[test]
366    fn inlinevec_drop_stack() {
367        let mut x = InlineVec::<String, 4>::new();
368
369        for i in 0..3u8 {
370            x.push(i.to_string());
371        }
372
373        assert_eq!(x.as_slice(), &["0", "1", "2"]);
374        assert!(!x.is_heap_allocated());
375    }
376
377    #[test]
378    fn inlinehashmap_drop_heap() {
379        let mut x = InlineVec::<String, 4>::new();
380
381        for i in 0..8u8 {
382            x.push(i.to_string());
383        }
384
385        assert_eq!(x.as_slice(), &["0", "1", "2", "3", "4", "5", "6", "7"]);
386        assert!(x.is_heap_allocated());
387    }
388
389    #[test]
390    fn inlinevec_iter() {
391        let mut x = InlineVecInner::<usize, 2>::new();
392        x.push(13);
393        x.push(42);
394        x.push(17);
395        x.push(19);
396        let mut iter = x.iter();
397        assert_eq!(iter.next(), Some(&13));
398        assert_eq!(iter.next(), Some(&42));
399        assert_eq!(iter.next(), Some(&17));
400        assert_eq!(iter.next(), Some(&19));
401        assert_eq!(iter.next(), None);
402    }
403
404    #[test]
405    fn inlinevec_remove() {
406        let mut x = InlineVecInner::<usize, 4>::new();
407        x.push(789);
408        assert_eq!(x.len(), 1);
409        assert_eq!(x.get(0), Some(&789));
410        assert_eq!(x.remove(0), 789);
411        assert_eq!(x.len(), 0);
412
413        {
414            let mut xc = x.clone();
415            // out of bounds index must panic
416            assert!(std::panic::catch_unwind(move || xc.remove(0)).is_err());
417        }
418
419        for i in 0..4 {
420            x.push(i * 2);
421        }
422
423        assert!(!x.is_heap_allocated());
424        assert_eq!(x.as_slice(), &[0, 2, 4, 6]);
425
426        assert_eq!(x.remove(2), 4);
427        assert_eq!(x.as_slice(), &[0, 2, 6]);
428
429        assert_eq!(x.remove(2), 6);
430        assert_eq!(x.as_slice(), &[0, 2]);
431
432        assert_eq!(x.remove(1), 2);
433        assert_eq!(x.as_slice(), &[0]);
434
435        assert_eq!(x.remove(0), 0);
436        assert_eq!(x.as_slice(), &[]);
437        assert!(!x.is_heap_allocated());
438
439        // trigger heap allocation
440        for i in 0..8 {
441            x.push(i * 2);
442        }
443        assert!(x.is_heap_allocated());
444        assert_eq!(x.as_slice(), &[0, 2, 4, 6, 8, 10, 12, 14]);
445
446        assert_eq!(x.remove(7), 14);
447        assert_eq!(x.remove(0), 0);
448    }
449
450    #[test]
451    fn inlinevec_remove_heap() {
452        let mut x = InlineVecInner::<String, 4>::new();
453        x.push("test".into());
454        assert_eq!(x.len(), 1);
455        assert_eq!(x.remove(0), "test");
456        assert_eq!(x.len(), 0);
457    }
458
459    #[test]
460    fn inlinevec() {
461        let mut x = InlineVecInner::<usize, 4>::new();
462        assert_eq!(x.len(), 0);
463        assert_eq!(x.get(0), None);
464        assert!(!x.is_heap_allocated());
465
466        x.push(1337);
467        assert_eq!(x.len(), 1);
468        assert_eq!(x.get(0), Some(&1337));
469        assert!(!x.is_heap_allocated());
470
471        for v in 0..3 {
472            x.push(v);
473        }
474
475        assert_eq!(x.len(), 4);
476
477        // this call should move the vector to the heap
478        x.push(42);
479        assert_eq!(x.len(), 5);
480        assert!(x.is_heap_allocated());
481
482        // check that the old vector is still valid
483        assert_eq!(x.get(0), Some(&1337));
484
485        for v in 0..500 {
486            x.push(v);
487        }
488
489        assert_eq!(x.len(), 505);
490        assert!(x.is_heap_allocated());
491
492        assert_eq!(x.get(1337), None);
493
494        *x.get_mut(0).unwrap() = 444;
495        assert_eq!(x.get(0), Some(&444));
496        assert_eq!(x.get_mut(99999 /* out of bounds */), None);
497    }
498
499    #[test]
500    fn inlinevec_as_slice() {
501        let mut x = InlineVecInner::<usize, 4>::new();
502        x.push(1337);
503        x.push(42);
504        x.push(17);
505        assert_eq!(x.as_slice(), &[1337, 42, 17]);
506        x.push(19);
507        x.push(34);
508        assert_eq!(x.as_slice(), &[1337, 42, 17, 19, 34]);
509    }
510}