use core::{borrow, cmp, hash, ops, ptr, slice};
use crate::uninit::Uninit;
pub struct FixedVec<'a, T> {
uninit: Uninit<'a, [T]>,
length: usize,
}
pub struct Drain<'a, T> {
count: usize,
tail: usize,
tail_len: usize,
elements: ptr::NonNull<T>,
len: &'a mut usize,
}
impl<T> FixedVec<'_, T> {
pub fn truncate(&mut self, len: usize) {
struct SetLenOnDrop<'a> {
len: &'a mut usize,
local_len: usize,
}
impl Drop for SetLenOnDrop<'_> {
fn drop(&mut self) {
*self.len = self.local_len;
}
}
let mut ptr = self.end_mut_ptr();
let current_length = self.length;
let mut set_len = SetLenOnDrop { len: &mut self.length, local_len: current_length };
for _ in len..current_length {
set_len.local_len -= 1;
unsafe {
ptr = ptr.offset(-1);
ptr::drop_in_place(ptr);
}
}
}
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 clear(&mut self) {
self.truncate(0)
}
pub fn len(&self) -> usize {
self.length
}
pub unsafe fn set_len(&mut self, new_len: usize) {
self.length = new_len;
}
pub fn capacity(&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)
}
#[must_use = "Elements in the split tail will be dropped. Prefer `truncate(at)` or `drain(at..)` if there is no other use."]
pub fn split_borrowed(&mut self, at: usize) -> FixedVec<'_, T> {
assert!(at <= self.capacity(), "`at` out of bounds");
let new_uninit = self.uninit.borrow_mut().split_at(at).unwrap();
let new_len = self.length.saturating_sub(at);
self.length -= new_len;
FixedVec {
uninit: new_uninit,
length: new_len,
}
}
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 -= new_len;
FixedVec {
uninit: new_uninit,
length: new_len,
}
}
pub fn fill<I: IntoIterator<Item=T>>(&mut self, iter: I)
-> I::IntoIter
{
let unused = self.capacity() - self.len();
let mut iter = iter.into_iter();
for item in iter.by_ref().take(unused) {
unsafe {
*self.end_mut_ptr() = item;
self.set_len(self.length + 1);
}
}
iter
}
pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
where R: ops::RangeBounds<usize>
{
let len = self.len();
let start = match range.start_bound() {
ops::Bound::Included(&n) => n,
ops::Bound::Excluded(&n) => n + 1,
ops::Bound::Unbounded => 0,
};
let end = match range.end_bound() {
ops::Bound::Included(&n) => n + 1,
ops::Bound::Excluded(&n) => n,
ops::Bound::Unbounded => len,
};
assert!(start <= end);
assert!(end <= len);
let elements = unsafe {
self.set_len(start);
ptr::NonNull::new_unchecked(self.as_mut_ptr().add(start))
};
Drain {
count: 0,
tail: end - start,
tail_len: len - end,
elements,
len: &mut self.length,
}
}
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)
}
fn end_mut_ptr(&mut self) -> *mut T {
unsafe { self.as_mut_ptr().add(self.length) }
}
}
impl<'a, T> FixedVec<'a, T> {
pub fn new(uninit: Uninit<'a, [T]>) -> Self {
FixedVec {
uninit,
length: 0,
}
}
pub fn from_unaligned<U: ?Sized>(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> Drain<'_, T> {
pub fn as_slice(&self) -> &[T] {
unsafe {
slice::from_raw_parts(
self.elements.as_ptr().add(self.count),
self.tail - self.count)
}
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(
self.elements.as_ptr().add(self.count),
self.tail - self.count)
}
}
}
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())
}
}
}
impl<T, I> ops::Index<I> for FixedVec<'_, T>
where I: slice::SliceIndex<[T]>,
{
type Output = I::Output;
fn index(&self, idx: I) -> &I::Output {
ops::Index::index(&**self, idx)
}
}
impl<T, I> ops::IndexMut<I> for FixedVec<'_, T>
where I: slice::SliceIndex<[T]>,
{
fn index_mut(&mut self, idx: I) -> &mut I::Output {
ops::IndexMut::index_mut(&mut**self, idx)
}
}
impl<'a, 'b, T: PartialEq> PartialEq<FixedVec<'b, T>> for FixedVec<'a, T> {
#[inline]
fn eq(&self, other: &FixedVec<T>) -> bool {
PartialEq::eq(&**self, &**other)
}
#[inline]
fn ne(&self, other: &FixedVec<T>) -> bool {
PartialEq::ne(&**self, &**other)
}
}
impl<'a, 'b, T: PartialOrd> PartialOrd<FixedVec<'b, T>> for FixedVec<'a, T> {
#[inline]
fn partial_cmp(&self, other: &FixedVec<T>) -> Option<cmp::Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
#[inline]
fn lt(&self, other: &FixedVec<T>) -> bool {
PartialOrd::lt(&**self, &**other)
}
#[inline]
fn le(&self, other: &FixedVec<T>) -> bool {
PartialOrd::le(&**self, &**other)
}
#[inline]
fn ge(&self, other: &FixedVec<T>) -> bool {
PartialOrd::ge(&**self, &**other)
}
#[inline]
fn gt(&self, other: &FixedVec<T>) -> bool {
PartialOrd::gt(&**self, &**other)
}
}
impl<T: Ord> Ord for FixedVec<'_, T> {
#[inline]
fn cmp(&self, other: &FixedVec<T>) -> cmp::Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: Eq> Eq for FixedVec<'_, T> { }
impl<T: hash::Hash> hash::Hash for FixedVec<'_, T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
hash::Hash::hash(&**self, state)
}
}
impl<T> borrow::Borrow<[T]> for FixedVec<'_, T> {
fn borrow(&self) -> &[T] {
&**self
}
}
impl<T> borrow::BorrowMut<[T]> for FixedVec<'_, T> {
fn borrow_mut(&mut self) -> &mut [T] {
&mut **self
}
}
impl<T> AsRef<[T]> for FixedVec<'_, T> {
fn as_ref(&self) -> &[T] {
&**self
}
}
impl<T> AsMut<[T]> for FixedVec<'_, T> {
fn as_mut(&mut self) -> &mut [T] {
&mut **self
}
}
impl<T> Iterator for Drain<'_, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.count == self.tail {
return None;
}
let t = unsafe {
ptr::read(self.elements.as_ptr().add(self.count))
};
self.count += 1;
Some(t)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.count..self.tail).size_hint()
}
}
impl<T> Drop for Drain<'_, T> {
fn drop(&mut self) {
self.for_each(drop);
if self.tail_len != 0 {
unsafe {
let source = self.elements.as_ptr().add(self.tail);
ptr::copy(source, self.elements.as_ptr(), self.tail_len);
}
*self.len += self.tail_len;
}
}
}
#[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(Clone, Copy, Debug, PartialEq, Eq)]
struct Foo(usize);
const CAPACITY: usize = 30;
let mut allocation: [MaybeUninit<Foo>; 30] = [MaybeUninit::uninit(); 30];
let mut vec = FixedVec::new((&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 uninit = Uninit::from_maybe_uninit(&mut allocation);
let mut vec = FixedVec::new(uninit.cast_slice().unwrap());
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 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::new(aligned.cast_slice().unwrap());
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]);
}
#[test]
#[should_panic = "Trigger triggered"]
fn drop_safe_in_truncation() {
use core::cell::Cell;
#[derive(Debug)]
struct Trigger<'a> {
panic_on_drop: bool,
dropped_counter: &'a Cell<usize>,
}
impl Drop for Trigger<'_> {
fn drop(&mut self) {
if self.panic_on_drop { panic!("Trigger triggered") }
self.dropped_counter.set(self.dropped_counter.get() + 1);
}
}
struct AbortMismatchedDropCount<'a> {
counter: &'a Cell<usize>,
expected: usize,
}
impl Drop for AbortMismatchedDropCount<'_> {
fn drop(&mut self) {
struct ForceDupPanic;
impl Drop for ForceDupPanic {
fn drop(&mut self) { panic!() }
}
if self.expected != self.counter.get() {
let _x = ForceDupPanic;
panic!();
}
}
}
let mut allocation: MaybeUninit<[Trigger<'static>; 3]> = MaybeUninit::zeroed();
let drops = Cell::new(0);
let _abort_mismatch_raii = AbortMismatchedDropCount {
counter: &drops,
expected: 2,
};
let uninit = Uninit::from(&mut allocation).as_memory();
let mut vec = FixedVec::new(uninit.cast_slice().unwrap());
vec.push(Trigger { panic_on_drop: false, dropped_counter: &drops }).unwrap();
vec.push(Trigger { panic_on_drop: false, dropped_counter: &drops }).unwrap();
vec.push(Trigger { panic_on_drop: true, dropped_counter: &drops }).unwrap();
vec.truncate(1);
}
}