use core::{ops, ptr, slice};
use crate::uninit::Uninit;
pub struct FixedVec<'a, T> {
uninit: Uninit<'a, [T]>,
length: usize,
}
impl<T> FixedVec<'_, T> {
pub fn as_slice(&self) -> &[T] {
unsafe {
slice::from_raw_parts(self.uninit.as_begin_ptr(), self.length)
}
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(self.uninit.as_begin_ptr(), self.length)
}
}
pub fn len(&self) -> usize {
self.length
}
pub fn capacity(&mut self) -> usize {
self.uninit.capacity()
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn push(&mut self, val: T) -> Result<(), T> {
if self.length == usize::max_value() {
return Err(val);
}
let init = match self.head_tail_mut().1.split_first() {
Some(init) => init,
None => return Err(val),
};
init.init(val);
self.length += 1;
Ok(())
}
pub fn pop(&mut self) -> Option<T> {
if self.length == 0 {
return None;
}
let last = self.head_tail_mut().0.split_last().unwrap();
let val = unsafe {
ptr::read(last.as_ptr())
};
self.length -= 1;
Some(val)
}
pub fn split_and_shrink_to(&mut self, at: usize) -> Self {
assert!(at <= self.capacity(), "`at` out of bounds");
let new_uninit = self.uninit.split_at(at).unwrap();
let new_len = self.length.saturating_sub(at);
self.length = self.length - new_len;
FixedVec {
uninit: new_uninit,
length: new_len,
}
}
fn head_tail_mut(&mut self) -> (Uninit<'_, [T]>, Uninit<'_, [T]>) {
let mut all = self.uninit.borrow_mut();
let tail = all.split_at(self.length).unwrap();
(all, tail)
}
}
impl<'a, T> FixedVec<'a, T> {
pub fn new(uninit: Uninit<'a, [T]>) -> Self {
FixedVec {
uninit,
length: 0,
}
}
pub fn from_available<U>(generic: Uninit<'a, U>) -> Self {
let mut uninit = generic.as_memory();
let slice = uninit.split_slice().unwrap_or_else(Uninit::empty);
Self::new(slice)
}
pub fn shrink_to_fit(&mut self) -> Uninit<'a, ()> {
self.uninit.shrink_to_fit()
}
}
impl<T> ops::Deref for FixedVec<'_, T> {
type Target = [T];
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> ops::DerefMut for FixedVec<'_, T> {
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T> Drop for FixedVec<'_, T> {
fn drop(&mut self) {
unsafe {
ptr::drop_in_place(self.as_mut_slice())
}
}
}
#[cfg(test)]
mod tests {
use super::FixedVec;
use crate::Uninit;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn init_and_use() {
#[derive(Debug, PartialEq, Eq)]
struct Foo(usize);
const CAPACITY: usize = 30;
let mut allocation: MaybeUninit<[Foo; CAPACITY]> = MaybeUninit::uninit();
let mut vec = FixedVec::from_available((&mut allocation).into());
assert_eq!(vec.capacity(), CAPACITY);
assert_eq!(vec.len(), 0);
for i in 0..CAPACITY {
assert_eq!(vec.push(Foo(i)), Ok(()));
}
assert_eq!(vec.capacity(), CAPACITY);
assert_eq!(vec.len(), CAPACITY);
for i in (0..CAPACITY).rev() {
assert_eq!(vec.pop(), Some(Foo(i)));
}
assert_eq!(vec.capacity(), CAPACITY);
assert_eq!(vec.len(), 0);
}
#[test]
fn zst_drop() {
const COUNT: usize = 30;
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct HasDrop(usize);
impl Drop for HasDrop {
fn drop(&mut self) {
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
}
}
let mut allocation: MaybeUninit<[HasDrop; COUNT]> = MaybeUninit::uninit();
let mut vec = FixedVec::from_available((&mut allocation).into());
for i in 0..COUNT {
assert!(vec.push(HasDrop(i)).is_ok());
}
drop(vec);
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), COUNT);
}
#[test]
fn zst() {
struct Zst;
let mut vec = FixedVec::<Zst>::new(Uninit::empty());
assert_eq!(vec.capacity(), usize::max_value());
}
#[test]
fn split_and_shrink() {
let mut allocation: MaybeUninit<[u16; 8]> = MaybeUninit::zeroed();
let mut aligned = Uninit::from(&mut allocation).as_memory();
let _ = aligned.split_at_byte(15);
let mut vec = FixedVec::from_available(aligned);
let mut second = vec.split_and_shrink_to(4);
let tail = second.shrink_to_fit();
assert_eq!(vec.capacity(), 4);
assert_eq!(vec.shrink_to_fit().size(), 0);
assert_eq!(second.capacity(), 3);
assert_eq!(second.shrink_to_fit().size(), 0);
assert_eq!(tail.size(), 1);
let _ = tail.cast::<u8>().unwrap().init(0xFF);
(0_u16..4).for_each(|v| assert!(vec.push(v).is_ok()));
(4..7).for_each(|v| assert!(second.push(v).is_ok()));
assert_eq!(vec.len(), 4);
assert_eq!(second.len(), 3);
drop(vec);
drop(second);
assert_eq!(
&unsafe { *allocation.as_ptr() }[..7],
[0, 1, 2, 3, 4, 5, 6]);
}
}