use std::mem::MaybeUninit;
pub struct TinyArray<T, const N: usize>
where
[(); N]:
{
len: u8,
slots: [MaybeUninit<T>; N],
}
impl<T: Copy, const N: usize> Clone for TinyArray<T, N>
where
[(); N]:
{
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T: Copy, const N: usize> Copy for TinyArray<T, N> where [(); N]: {}
impl<T: std::fmt::Debug, const N: usize> std::fmt::Debug for TinyArray<T, N>
where
[(); N]:
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.as_slice()).finish()
}
}
#[allow(dead_code)]
impl<T, const N: usize> TinyArray<T, N>
where
[(); N]:
{
pub fn new() -> Self {
Self {
len: 0,
slots: unsafe { MaybeUninit::uninit().assume_init() },
}
}
#[inline]
pub fn len(&self) -> usize {
self.len as usize
}
#[inline]
pub fn is_full(&self) -> bool {
self.len as usize == N
}
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.slots[0].as_ptr(), self.len as usize) }
}
#[inline]
pub fn get(&self, i: usize) -> &T {
debug_assert!(i < self.len as usize, "TinyArray::get: index out of bounds");
unsafe { &*self.slots[i].as_ptr() }
}
#[inline]
pub unsafe fn get_unchecked(&self, i: usize) -> &T {
unsafe { &*self.slots[i].as_ptr() }
}
#[inline]
pub fn get_mut(&mut self, i: usize) -> &mut T {
debug_assert!(i < self.len as usize, "TinyArray::get_mut: index out of bounds");
unsafe { &mut *self.slots[i].as_mut_ptr() }
}
pub fn insert_at(&mut self, pos: usize, val: T) {
debug_assert!(!self.is_full(), "TinyArray::insert_at: array is full");
debug_assert!(pos <= self.len as usize, "TinyArray::insert_at: pos out of bounds");
let l = self.len as usize;
if pos < l {
unsafe {
std::ptr::copy(
self.slots[pos].as_ptr(),
self.slots[pos + 1].as_mut_ptr(),
l - pos,
);
}
}
unsafe {
self.slots[pos].as_mut_ptr().write(val);
}
self.len += 1;
}
pub fn remove_at(&mut self, pos: usize) -> T {
debug_assert!(pos < self.len as usize, "TinyArray::remove_at: index out of bounds");
let l = self.len as usize;
let val = unsafe { self.slots[pos].assume_init_read() };
if pos + 1 < l {
unsafe {
std::ptr::copy(
self.slots[pos + 1].as_ptr(),
self.slots[pos].as_mut_ptr(),
l - pos - 1,
);
}
}
self.len -= 1;
val
}
pub fn push(&mut self, val: T) {
self.insert_at(self.len as usize, val);
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
self.len -= 1;
Some(unsafe { self.slots[self.len as usize].assume_init_read() })
}
#[inline]
pub fn truncate(&mut self, new_len: u8) {
debug_assert!(new_len as usize <= N, "TinyArray::truncate: new_len exceeds capacity");
self.len = new_len;
}
pub fn permute_in_place(&mut self, perm: &[usize]) {
let n = self.len as usize;
if n <= 1 {
return;
}
let mut p: [usize; N] = [0; N];
for i in 0..n {
p[i] = perm[i];
debug_assert!(perm[i] < n, "TinyArray::permute_in_place: perm out of range");
}
for i in 0..n {
let mut j = i;
while p[j] != i {
unsafe {
std::ptr::swap(
self.slots[j].as_mut_ptr(),
self.slots[p[j]].as_mut_ptr(),
);
}
let next = p[j];
p[j] = j; j = next;
}
p[j] = j;
}
}
#[inline]
pub unsafe fn read_slot(&mut self, pos: usize) -> T {
debug_assert!(pos < self.len as usize, "TinyArray::read_slot: index out of bounds");
unsafe { self.slots[pos].assume_init_read() }
}
pub fn drain_into(&mut self, from: usize, dst: &mut Self) {
let count = self.len as usize - from;
debug_assert!(from < self.len as usize, "TinyArray::drain_into: empty drain");
debug_assert!(dst.len as usize + count <= N, "TinyArray::drain_into: dst overflow");
unsafe {
std::ptr::copy_nonoverlapping(
self.slots[from].as_ptr(),
dst.slots[dst.len as usize].as_mut_ptr(),
count,
);
}
dst.len += count as u8;
self.len = from as u8;
}
pub fn drain_into_front(&mut self, from: usize, dst: &mut Self) {
let count = self.len as usize - from;
debug_assert!(from < self.len as usize, "TinyArray::drain_into_front: empty drain");
debug_assert!(
dst.len as usize + count <= N,
"TinyArray::drain_into_front: dst overflow"
);
unsafe {
if dst.len as usize != 0 {
std::ptr::copy(
dst.slots[0].as_ptr(),
dst.slots[count].as_mut_ptr(),
dst.len as usize,
);
}
std::ptr::copy_nonoverlapping(
self.slots[from].as_ptr(),
dst.slots[0].as_mut_ptr(),
count,
);
}
dst.len += count as u8;
self.len = from as u8;
}
pub fn drain_front_into(&mut self, count: usize, dst: &mut Self) {
debug_assert!(count <= self.len as usize, "TinyArray::drain_front_into: count out of bounds");
debug_assert!(
dst.len as usize + count <= N,
"TinyArray::drain_front_into: dst overflow"
);
let l = self.len as usize;
unsafe {
std::ptr::copy_nonoverlapping(
self.slots[0].as_ptr(),
dst.slots[dst.len as usize].as_mut_ptr(),
count,
);
if count < l {
std::ptr::copy(
self.slots[count].as_ptr(),
self.slots[0].as_mut_ptr(),
l - count,
);
}
}
dst.len += count as u8;
self.len -= count as u8;
}
}
impl<T, const N: usize> Default for TinyArray<T, N>
where
[(); N]:
{
fn default() -> Self {
Self::new()
}
}