//! Internal "small" style collection types. use alloc::vec::Vec; use core::hash::{Hash, Hasher}; /// A growable vector type with inline storage optimization. /// /// Note that unlike the real `SmallVec`, this only works with types that /// are `Copy + Default` to simplify our implementation. #[derive(Clone)] pub(crate) struct SmallVec(Storage); impl SmallVec where T: Copy + Default, { /// Creates a new, empty `SmallVec`. pub fn new() -> Self { Self(Storage::Inline([T::default(); N], 0)) } /// Creates a new `SmallVec` of the given length with each element /// containing a copy of `value`. pub fn with_len(len: usize, value: T) -> Self { if len <= N { Self(Storage::Inline([value; N], len)) } else { let mut vec = Vec::new(); vec.resize(len, value); Self(Storage::Heap(vec)) } } /// Clears the vector, removing all values. pub fn clear(&mut self) { match &mut self.0 { Storage::Inline(_buf, len) => *len = 0, Storage::Heap(vec) => vec.clear(), } } /// Tries to reserve capacity for at least `additional` more elements. pub fn try_reserve(&mut self, additional: usize) -> bool { match &mut self.0 { Storage::Inline(buf, len) => { let new_cap = *len + additional; if new_cap > N { let mut vec = Vec::new(); if vec.try_reserve(new_cap).is_err() { return false; } vec.extend_from_slice(&buf[..*len]); self.0 = Storage::Heap(vec); } } Storage::Heap(vec) => { if vec.try_reserve(additional).is_err() { return false; } } } true } /// Appends an element to the back of the collection. pub fn push(&mut self, value: T) { match &mut self.0 { Storage::Inline(buf, len) => { if *len + 1 > N { let mut vec = Vec::with_capacity(*len + 1); vec.extend_from_slice(&buf[..*len]); vec.push(value); self.0 = Storage::Heap(vec); } else { buf[*len] = value; *len += 1; } } Storage::Heap(vec) => vec.push(value), } } /// Removes and returns the value at the back of the collection. pub fn pop(&mut self) -> Option { match &mut self.0 { Storage::Inline(buf, len) => { if *len > 0 { *len -= 1; Some(buf[*len]) } else { None } } Storage::Heap(vec) => vec.pop(), } } /// Shortens the vector, keeping the first `len` elements. pub fn truncate(&mut self, len: usize) { match &mut self.0 { Storage::Inline(_buf, inline_len) => { *inline_len = len.min(*inline_len); } Storage::Heap(vec) => vec.truncate(len), } } /// Resizes the vector to `len` elements, filling with `value`. /// Reuses existing heap allocation when possible. pub fn resize_and_fill(&mut self, len: usize, value: T) { match &mut self.0 { Storage::Inline(buf, inline_len) => { if len <= N { buf[..len].fill(value); *inline_len = len; } else { // Need to spill to heap let mut vec = Vec::with_capacity(len); vec.resize(len, value); self.0 = Storage::Heap(vec); } } Storage::Heap(vec) => { vec.clear(); vec.resize(len, value); } } } } impl SmallVec { /// Extracts a slice containing the entire vector. pub fn as_slice(&self) -> &[T] { match &self.0 { Storage::Inline(buf, len) => &buf[..*len], Storage::Heap(vec) => vec.as_slice(), } } /// Extracts a mutable slice containing the entire vector. pub fn as_mut_slice(&mut self) -> &mut [T] { match &mut self.0 { Storage::Inline(buf, len) => &mut buf[..*len], Storage::Heap(vec) => vec.as_mut_slice(), } } } impl Default for SmallVec where T: Copy + Default, { fn default() -> Self { Self::new() } } impl core::ops::Deref for SmallVec { type Target = [T]; fn deref(&self) -> &Self::Target { self.as_slice() } } impl core::ops::DerefMut for SmallVec { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_slice() } } impl Hash for SmallVec where T: Hash, { fn hash(&self, state: &mut H) { self.as_slice().hash(state); } } impl PartialEq for SmallVec where T: PartialEq, { fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() } } impl PartialEq<[T]> for SmallVec where T: PartialEq, { fn eq(&self, other: &[T]) -> bool { self.as_slice() == other } } impl Eq for SmallVec where T: Eq {} impl core::fmt::Debug for SmallVec where T: core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_list().entries(self.as_slice().iter()).finish() } } impl<'a, T, const N: usize> IntoIterator for &'a SmallVec { type IntoIter = core::slice::Iter<'a, T>; type Item = &'a T; fn into_iter(self) -> Self::IntoIter { self.as_slice().iter() } } impl<'a, T, const N: usize> IntoIterator for &'a mut SmallVec { type IntoIter = core::slice::IterMut<'a, T>; type Item = &'a mut T; fn into_iter(self) -> Self::IntoIter { self.as_mut_slice().iter_mut() } } impl IntoIterator for SmallVec where T: Copy, { type IntoIter = IntoIter; type Item = T; fn into_iter(self) -> Self::IntoIter { IntoIter { vec: self, pos: 0 } } } #[derive(Clone)] pub(crate) struct IntoIter { vec: SmallVec, pos: usize, } impl Iterator for IntoIter where T: Copy, { type Item = T; fn next(&mut self) -> Option { let value = self.vec.get(self.pos)?; self.pos += 1; Some(*value) } } #[derive(Clone)] enum Storage { Inline([T; N], usize), Heap(Vec), } #[cfg(test)] mod test { use super::{SmallVec, Storage}; #[test] fn choose_inline() { let vec = SmallVec::<_, 4>::with_len(4, 0); assert!(matches!(vec.0, Storage::Inline(..))); assert_eq!(vec.len(), 4); } #[test] fn choose_heap() { let vec = SmallVec::<_, 4>::with_len(5, 0); assert!(matches!(vec.0, Storage::Heap(..))); assert_eq!(vec.len(), 5); } #[test] fn store_and_read_inline() { let mut vec = SmallVec::<_, 8>::with_len(8, 0); for (i, value) in vec.iter_mut().enumerate() { *value = i * 2; } let expected = [0, 2, 4, 6, 8, 10, 12, 14]; assert_eq!(vec.as_slice(), &expected); assert_eq!(format!("{vec:?}"), format!("{expected:?}")); } #[test] fn store_and_read_heap() { let mut vec = SmallVec::<_, 4>::with_len(8, 0); for (i, value) in vec.iter_mut().enumerate() { *value = i * 2; } let expected = [0, 2, 4, 6, 8, 10, 12, 14]; assert_eq!(vec.as_slice(), &expected); assert_eq!(format!("{vec:?}"), format!("{expected:?}")); } #[test] fn spill_to_heap() { let mut vec = SmallVec::<_, 4>::new(); for i in 0..4 { vec.push(i); } assert!(matches!(vec.0, Storage::Inline(..))); vec.push(4); assert!(matches!(vec.0, Storage::Heap(..))); let expected = [0, 1, 2, 3, 4]; assert_eq!(vec.as_slice(), &expected); } #[test] fn clear_inline() { let mut vec = SmallVec::<_, 4>::new(); for i in 0..4 { vec.push(i); } assert!(matches!(vec.0, Storage::Inline(..))); assert_eq!(vec.len(), 4); vec.clear(); assert_eq!(vec.len(), 0); } #[test] fn clear_heap() { let mut vec = SmallVec::<_, 3>::new(); for i in 0..4 { vec.push(i); } assert!(matches!(vec.0, Storage::Heap(..))); assert_eq!(vec.len(), 4); vec.clear(); assert_eq!(vec.len(), 0); } #[test] fn reserve() { let mut vec = SmallVec::<_, 3>::new(); for i in 0..2 { vec.push(i); } assert!(matches!(vec.0, Storage::Inline(..))); assert!(vec.try_reserve(1)); // still inline after reserving 1 assert!(matches!(vec.0, Storage::Inline(..))); assert!(vec.try_reserve(2)); // reserving 2 spills to heap assert!(matches!(vec.0, Storage::Heap(..))); } #[test] fn iter() { let mut vec = SmallVec::<_, 3>::new(); for i in 0..3 { vec.push(i); } assert!(&[0, 1, 2].iter().eq(vec.iter())); } #[test] fn into_iter() { let mut vec = SmallVec::<_, 3>::new(); for i in 0..3 { vec.push(i); } assert!([0, 1, 2].into_iter().eq(vec.into_iter())); } }