use std::fmt;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut, Index, IndexMut};
enum Storage<T, const N: usize> {
Inline {
data: [MaybeUninit<T>; N],
len: usize,
},
Heap(Vec<T>),
}
pub struct TinyVec<T, const N: usize> {
storage: Storage<T, N>,
}
impl<T, const N: usize> TinyVec<T, N> {
pub fn new() -> Self {
TinyVec {
storage: Storage::Inline {
data: unsafe { MaybeUninit::uninit().assume_init() },
len: 0,
},
}
}
pub fn from_vec(v: Vec<T>) -> Self {
TinyVec {
storage: Storage::Heap(v),
}
}
pub fn len(&self) -> usize {
match &self.storage {
Storage::Inline { len, .. } => *len,
Storage::Heap(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_inline(&self) -> bool {
matches!(&self.storage, Storage::Inline { .. })
}
pub fn push(&mut self, value: T) {
match &mut self.storage {
Storage::Inline { data, len } => {
if *len < N {
unsafe {
data[*len].as_mut_ptr().write(value);
}
*len += 1;
} else {
self.spill_to_heap(value);
}
}
Storage::Heap(v) => v.push(value),
}
}
pub fn pop(&mut self) -> Option<T> {
match &mut self.storage {
Storage::Inline { data, len } => {
if *len == 0 {
return None;
}
*len -= 1;
let value = unsafe { data[*len].as_ptr().read() };
Some(value)
}
Storage::Heap(v) => v.pop(),
}
}
pub fn get(&self, index: usize) -> Option<&T> {
if index >= self.len() {
return None;
}
match &self.storage {
Storage::Inline { data, .. } => Some(unsafe { &*data[index].as_ptr() }),
Storage::Heap(v) => v.get(index),
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index >= self.len() {
return None;
}
match &mut self.storage {
Storage::Inline { data, .. } => Some(unsafe { &mut *data[index].as_mut_ptr() }),
Storage::Heap(v) => v.get_mut(index),
}
}
pub fn as_slice(&self) -> &[T] {
match &self.storage {
Storage::Inline { data, len } => unsafe {
std::slice::from_raw_parts(data.as_ptr() as *const T, *len)
},
Storage::Heap(v) => v.as_slice(),
}
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
match &mut self.storage {
Storage::Inline { data, len } => unsafe {
std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut T, *len)
},
Storage::Heap(v) => v.as_mut_slice(),
}
}
pub fn clear(&mut self) {
match &mut self.storage {
Storage::Inline { data, len } => {
for i in 0..*len {
unsafe { data[i].as_mut_ptr().drop_in_place() };
}
*len = 0;
}
Storage::Heap(v) => v.clear(),
}
}
pub fn into_vec(mut self) -> Vec<T> {
let storage = unsafe { std::ptr::read(&self.storage) };
std::mem::forget(self);
match storage {
Storage::Inline { data, len } => {
let mut v = Vec::with_capacity(len);
for i in 0..len {
v.push(unsafe { data[i].as_ptr().read() });
}
v
}
Storage::Heap(v) => v,
}
}
fn spill_to_heap(&mut self, extra: T) {
let old_storage = std::mem::replace(
&mut self.storage,
Storage::Heap(Vec::new()),
);
if let Storage::Inline { data, len } = old_storage {
let mut v = Vec::with_capacity(len + 1);
for i in 0..len {
v.push(unsafe { data[i].as_ptr().read() });
}
v.push(extra);
self.storage = Storage::Heap(v);
}
}
}
impl<T, const N: usize> Drop for TinyVec<T, N> {
fn drop(&mut self) {
if let Storage::Inline { data, len } = &mut self.storage {
for i in 0..*len {
unsafe { data[i].as_mut_ptr().drop_in_place() };
}
*len = 0;
}
}
}
impl<T, const N: usize> Deref for TinyVec<T, N> {
type Target = [T];
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T, const N: usize> DerefMut for TinyVec<T, N> {
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T, const N: usize> Index<usize> for TinyVec<T, N> {
type Output = T;
fn index(&self, index: usize) -> &T {
self.get(index).expect("TinyVec: index out of bounds")
}
}
impl<T, const N: usize> IndexMut<usize> for TinyVec<T, N> {
fn index_mut(&mut self, index: usize) -> &mut T {
self.get_mut(index).expect("TinyVec: index out of bounds")
}
}
impl<T: Clone, const N: usize> Clone for TinyVec<T, N> {
fn clone(&self) -> Self {
let mut out = TinyVec::new();
for elem in self.as_slice() {
out.push(elem.clone());
}
out
}
}
impl<T: fmt::Debug, const N: usize> fmt::Debug for TinyVec<T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_slice(), f)
}
}
impl<T: PartialEq, const N: usize> PartialEq for TinyVec<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<T: Eq, const N: usize> Eq for TinyVec<T, N> {}
impl<T, const N: usize> FromIterator<T> for TinyVec<T, N> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut v = TinyVec::new();
for item in iter {
v.push(item);
}
v
}
}
impl<T, const N: usize> IntoIterator for TinyVec<T, N> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.into_vec().into_iter()
}
}
impl<'a, T, const N: usize> IntoIterator for &'a TinyVec<T, N> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.as_slice().iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push_within_inline() {
let mut v: TinyVec<i32, 4> = TinyVec::new();
v.push(10);
v.push(20);
v.push(30);
assert!(v.is_inline());
assert_eq!(v.len(), 3);
assert_eq!(v[0], 10);
assert_eq!(v[2], 30);
}
#[test]
fn test_spill_to_heap() {
let mut v: TinyVec<i32, 2> = TinyVec::new();
v.push(1);
v.push(2);
assert!(v.is_inline());
v.push(3); assert!(!v.is_inline());
assert_eq!(v.len(), 3);
assert_eq!(v[0], 1);
assert_eq!(v[1], 2);
assert_eq!(v[2], 3);
}
#[test]
fn test_pop() {
let mut v: TinyVec<i32, 4> = TinyVec::new();
v.push(42);
assert_eq!(v.pop(), Some(42));
assert_eq!(v.pop(), None);
}
#[test]
fn test_pop_after_spill() {
let mut v: TinyVec<i32, 1> = TinyVec::new();
v.push(1);
v.push(2);
assert_eq!(v.pop(), Some(2));
assert_eq!(v.pop(), Some(1));
assert_eq!(v.pop(), None);
}
#[test]
fn test_clear() {
let mut v: TinyVec<String, 4> = TinyVec::new();
v.push("hello".to_string());
v.push("world".to_string());
v.clear();
assert!(v.is_empty());
}
#[test]
fn test_drop_non_copy() {
let mut v: TinyVec<String, 2> = TinyVec::new();
v.push("a".to_string());
v.push("b".to_string());
v.push("c".to_string()); drop(v);
}
#[test]
fn test_iter() {
let mut v: TinyVec<i32, 4> = TinyVec::new();
for i in 0..6 {
v.push(i);
}
let collected: Vec<_> = v.iter().copied().collect();
assert_eq!(collected, vec![0, 1, 2, 3, 4, 5]);
}
#[test]
fn test_from_iter() {
let v: TinyVec<i32, 4> = (0..8).collect();
assert_eq!(v.len(), 8);
for (i, &x) in v.iter().enumerate() {
assert_eq!(x, i as i32);
}
}
#[test]
fn test_clone() {
let mut v: TinyVec<i32, 4> = TinyVec::new();
v.push(1);
v.push(2);
let w = v.clone();
assert_eq!(v, w);
}
#[test]
fn test_zero_capacity() {
let mut v: TinyVec<i32, 0> = TinyVec::new();
v.push(99);
assert_eq!(v.len(), 1);
assert_eq!(v[0], 99);
}
}