#![no_std]
#![warn(clippy::pedantic)]
#![deny(missing_docs)]
extern crate alloc;
use alloc::alloc::{dealloc, handle_alloc_error, realloc};
use core::hint::assert_unchecked;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::{Bound, RangeBounds};
use core::ptr::{self, NonNull};
use core::{cmp, iter, slice};
#[cfg(test)]
mod tests;
mod polyfill;
#[allow(
clippy::wildcard_imports,
reason = "polyfill only contains a small number of unambiguous functions"
)]
use polyfill::*;
mod internal;
use internal::{Allocated, Inner, Large, ParsedMarker, Small};
mod into_iter;
pub use into_iter::IntoIter;
mod drain;
pub use drain::Drain;
mod retain;
#[cfg(feature = "serde")]
mod serde_impl;
#[cfg(all(test, feature = "serde"))]
mod serde_tests;
pub struct WordVec<T, const N: usize>(Inner<T, N>);
struct ReserveArgs {
len: usize,
cap: usize,
}
struct ShrinkToArgs {
len: usize,
}
impl<T, const N: usize> WordVec<T, N> {
#[must_use]
pub const fn new() -> Self {
Inner::<T, N>::assert_generics();
Self(Inner { small: ManuallyDrop::new(Small::new_uninit(0)) })
}
#[must_use]
pub const fn new_inlined<const LENGTH: usize>(values: [T; LENGTH]) -> Self {
const {
Inner::<T, N>::assert_generics();
assert!(LENGTH <= N, "new_inlined can only be used to create an inlined vector");
}
let mut data = [const { MaybeUninit::uninit() }; N];
let mut index = 0;
while index < LENGTH {
let value = &values[index];
unsafe { data[index].write(ptr::read(value)) };
index += 1;
}
mem::forget(values);
Self(Inner { small: ManuallyDrop::new(Small::new(LENGTH, data)) })
}
#[must_use]
pub fn with_capacity(cap: usize) -> Self {
Inner::<T, N>::assert_generics();
if cap <= N {
Self::default()
} else {
let large = Large::new_empty(cap);
Self(Inner { large: ManuallyDrop::new(large) })
}
}
pub fn as_slice(&self) -> &[T] {
match self.0.parse_marker() {
ParsedMarker::Small(len) => {
let small = unsafe { &self.0.small };
let uninit_slice = unsafe { small.data.get_unchecked(..usize::from(len)) };
unsafe { slice_assume_init_ref::<T>(uninit_slice) }
}
ParsedMarker::Large => {
let large = unsafe { &self.0.large };
let (allocated, data_start) = large.as_allocated();
unsafe { slice::from_raw_parts(data_start, allocated.len) }
}
}
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
match self.0.parse_marker() {
ParsedMarker::Small(len) => {
let small = unsafe { &mut self.0.small };
let uninit_slice = unsafe { small.data.get_unchecked_mut(..usize::from(len)) };
unsafe { slice_assume_init_mut::<T>(uninit_slice) }
}
ParsedMarker::Large => {
let large = unsafe { &mut self.0.large };
let (allocated, data_start) = large.as_allocated_mut();
unsafe { slice::from_raw_parts_mut(data_start, allocated.len) }
}
}
}
pub fn as_uninit_slice_with_length_setter(
&mut self,
) -> (&mut [MaybeUninit<T>], usize, LengthSetter<'_>) {
match self.0.parse_marker() {
ParsedMarker::Small(len) => {
let small = unsafe { &mut *self.0.small };
unsafe { assert_unchecked(len as usize <= N) };
(
&mut small.data[..],
usize::from(len),
LengthSetter {
inner: LengthSetterInner::Small(
&mut small.marker,
),
#[cfg(debug_assertions)]
capacity: N,
},
)
}
ParsedMarker::Large => {
let large = unsafe { &mut self.0.large };
let (allocated, data_start) = large.as_allocated_mut();
let slice = unsafe {
slice::from_raw_parts_mut(data_start.cast::<MaybeUninit<T>>(), allocated.cap)
};
unsafe { assert_unchecked(allocated.len <= allocated.cap) };
(
slice,
allocated.len,
LengthSetter {
inner: LengthSetterInner::Large(
&mut allocated.len,
),
#[cfg(debug_assertions)]
capacity: allocated.cap,
},
)
}
}
}
pub fn len(&self) -> usize {
match self.0.parse_marker() {
ParsedMarker::Small(len) => usize::from(len),
ParsedMarker::Large => {
let large = unsafe { &self.0.large };
large.as_allocated().0.len
}
}
}
pub fn is_empty(&self) -> bool { self.len() == 0 }
pub fn capacity(&self) -> usize {
match self.0.parse_marker() {
ParsedMarker::Small(_) => N,
ParsedMarker::Large => {
let large = unsafe { &self.0.large };
large.as_allocated().0.cap
}
}
}
pub fn push(&mut self, value: T) {
match self.0.parse_marker() {
ParsedMarker::Small(len) if usize::from(len) < N => {
let mut values = ManuallyDrop::new([value]);
unsafe { self.extend_small(&mut *values) }
}
ParsedMarker::Small(_) => {
unsafe {
self.move_small_to_large(N + 1);
}
let mut values = ManuallyDrop::new([value]);
unsafe { self.extend_large_slice(&mut *values) }
}
ParsedMarker::Large => {
let mut values = ManuallyDrop::new([value]);
unsafe { self.extend_large_slice(&mut *values) }
}
}
}
pub fn insert(&mut self, index: usize, value: T) {
self.reserve(1);
let (capacity_slice, len, mut set_len) = self.as_uninit_slice_with_length_setter();
assert!(index <= len, "insertion index (is {index}) should be <= len (is {len})");
#[expect(clippy::range_plus_one, reason = "len+1 is more explicit")]
let mutated_slice = &mut capacity_slice[index..len + 1];
let mutated_len = mutated_slice.len();
if !mutated_slice.is_empty() {
shift_right_once(&mut mutated_slice[..mutated_len]);
}
mutated_slice[0] = MaybeUninit::new(value);
unsafe {
set_len.set_len(len + 1);
}
}
unsafe fn extend_small(&mut self, values: *mut [T]) {
debug_assert!(self.len() + values.len() <= N);
let small = unsafe { &mut self.0.small };
let current_len = usize::from(small.marker >> 1);
let slice = &mut small.data.as_mut_slice()[current_len..current_len + values.len()];
unsafe {
ptr::copy_nonoverlapping(
values.cast::<T>(),
slice.as_mut_ptr().cast::<T>(),
values.len(),
);
}
let new_len =
(small.marker >> 1) + u8::try_from(values.len()).expect("values.len() <= N <= 127");
small.marker = (new_len << 1) | 1;
}
unsafe fn extend_small_iter(&mut self, values: impl Iterator<Item = T>) {
let small = unsafe { &mut self.0.small };
let current_len = usize::from(small.marker >> 1);
for (i, value) in values.enumerate() {
let dest = small.data.get_mut(current_len + i).expect("values has too many items");
dest.write(value);
small.marker += 2; }
}
unsafe fn extend_large_slice(&mut self, values: *mut [T]) {
let large = unsafe { &mut self.0.large };
let (&Allocated { len, cap, .. }, _) = large.as_allocated();
let new_len = len.checked_add(values.len()).expect("new length is out of bounds");
if new_len > cap {
large.grow(new_len);
}
unsafe {
Allocated::extend(large.0, values);
}
}
unsafe fn extend_large_iter(&mut self, values: impl Iterator<Item = T>) {
let large = unsafe { &mut *self.0.large };
large.extend_iter(values);
}
unsafe fn move_small_to_large(&mut self, new_cap: usize) {
let small = unsafe { &mut self.0.small };
let data = small.data.as_mut_ptr().cast::<T>();
let small_len = small.marker >> 1;
let data_slice = ptr::slice_from_raw_parts_mut(data, small_len.into());
let large = unsafe { Large::new(new_cap, data_slice) };
self.0.large = ManuallyDrop::new(large);
}
pub fn reserve(&mut self, additional: usize) {
self.reserve_with(|args| {
let req = args.len.checked_add(additional).expect("capacity overflow");
if req <= args.cap {
args.cap
} else if req <= args.cap * 2 {
args.cap * 2
} else {
req
}
});
}
pub fn reserve_exact(&mut self, additional: usize) {
self.reserve_with(|args| {
let req = args.len.checked_add(additional).expect("capacity overflow");
req.max(args.cap)
});
}
fn reserve_with(&mut self, get_new_cap: impl FnOnce(ReserveArgs) -> usize) {
match self.0.parse_marker() {
ParsedMarker::Small(len) => {
let len = usize::from(len);
let new_cap = get_new_cap(ReserveArgs { len, cap: N });
if new_cap > N {
unsafe {
self.move_small_to_large(new_cap);
}
}
}
ParsedMarker::Large => {
let large = unsafe { &mut self.0.large };
let (&Allocated { len, cap, .. }, _) = large.as_allocated();
let new_cap = get_new_cap(ReserveArgs { len, cap });
if new_cap > cap {
large.grow_exact(new_cap);
}
}
}
}
pub fn shrink_to_fit(&mut self) { self.shrink_to_with(|args| args.len.max(N)); }
pub fn shrink_to(&mut self, min_cap: usize) {
self.shrink_to_with(|args| args.len.max(min_cap).max(N));
}
fn shrink_to_with(&mut self, get_new_cap: impl FnOnce(ShrinkToArgs) -> usize) {
if let ParsedMarker::Small(_) = self.0.parse_marker() {
return; }
let large = unsafe { &mut *self.0.large };
let alloc_ptr = large.0;
let &Allocated { len, .. } = unsafe { alloc_ptr.as_ref() };
let large_layout = large.current_layout();
let new_cap = get_new_cap(ShrinkToArgs { len });
if new_cap == N {
let data_start = unsafe { Allocated::data_start(alloc_ptr) };
self.0.small = ManuallyDrop::new(Small::new_uninit(len));
unsafe {
ptr::copy_nonoverlapping(data_start, (*self.0.small).data.as_mut_ptr().cast(), len);
}
unsafe {
dealloc(alloc_ptr.as_ptr().cast(), large_layout);
}
} else {
let new_layout = Large::<T>::new_layout(new_cap);
let new_alloc_ptr =
unsafe { realloc(alloc_ptr.as_ptr().cast(), large_layout, new_layout.size()) };
match NonNull::new(new_alloc_ptr) {
None => {
handle_alloc_error(new_layout);
}
Some(new_alloc_ptr) => {
large.0 = new_alloc_ptr.cast();
unsafe {
large.0.as_mut().cap = new_cap;
}
}
}
}
}
#[inline]
pub fn remove(&mut self, index: usize) -> T {
match self.try_remove(index) {
Some(v) => v,
None => panic!("Cannot remove index {index} from length {}", self.len()),
}
}
#[inline]
pub fn try_remove(&mut self, index: usize) -> Option<T> {
let slice = self.remove_last_uninit(index)?;
let mutated_slice = &mut slice[index..];
let removed = unsafe { mutated_slice.first_mut().unwrap_unchecked().assume_init_read() };
shift_left_once(mutated_slice);
Some(removed)
}
pub fn swap_remove(&mut self, index: usize) -> T {
match self.try_swap_remove(index) {
Some(v) => v,
None => panic!("Cannot remove index {index} from length {}", self.len()),
}
}
pub fn try_swap_remove(&mut self, index: usize) -> Option<T> {
let slice = self.remove_last_uninit(index)?;
unsafe {
slice.swap(index, slice.len() - 1);
Some(slice.last_mut().unwrap_unchecked().assume_init_read())
}
}
pub fn pop(&mut self) -> Option<T> {
let last_index = self.len().checked_sub(1)?;
self.try_swap_remove(last_index)
}
fn remove_last_uninit(&mut self, len_gt: usize) -> Option<&mut [MaybeUninit<T>]> {
match self.0.parse_marker() {
ParsedMarker::Small(old_len) => {
let small = unsafe { &mut self.0.small };
if usize::from(old_len) <= len_gt {
return None;
}
small.marker = unsafe { small.marker.unchecked_sub(2) };
Some(&mut small.data[..usize::from(old_len)])
}
ParsedMarker::Large => {
let large = unsafe { &mut self.0.large };
let (allocated, data_start) = large.as_allocated_mut();
let old_len = allocated.len;
if old_len <= len_gt {
return None;
}
let new_len = old_len - 1; allocated.len = new_len;
let slice = unsafe {
slice::from_raw_parts_mut(data_start.cast::<MaybeUninit<T>>(), old_len)
};
Some(slice)
}
}
}
pub fn clear(&mut self) { self.truncate(0); }
pub fn truncate(&mut self, len: usize) {
let (capacity_slice, current_len, mut set_len) = self.as_uninit_slice_with_length_setter();
if current_len <= len {
return;
}
unsafe { set_len.set_len(len) };
unsafe { slice_assume_init_drop(&mut capacity_slice[len..current_len]) };
}
pub fn drain(&mut self, range: impl RangeBounds<usize>) -> Drain<'_, T> {
let (capacity_slice, current_len, mut set_len) = self.as_uninit_slice_with_length_setter();
let initial_slice = &mut capacity_slice[..current_len];
let (start_drain, end_drain) = resolve_range(range, current_len);
assert!(start_drain <= end_drain, "start drain index must not exceed end drain index");
assert!(end_drain <= current_len, "end drain index must not exceed current vector length");
unsafe { set_len.set_len(start_drain) };
let mutated_slice = &mut initial_slice[start_drain..];
let drained_offset = end_drain - start_drain;
Drain {
mutated_slice,
total_drained: drained_offset,
remain_start: 0,
remain_end: drained_offset,
set_len,
set_len_base: start_drain,
}
}
pub fn retain<F>(&mut self, mut should_retain: F)
where
F: FnMut(&mut T) -> bool,
{
let mut retain = retain::Retain::new(self, ..);
loop {
if let retain::NextResult::Exhausted = retain.next(&mut should_retain) {
break;
}
}
}
#[doc(alias = "drain_filter")]
pub fn extract_if(
&mut self,
range: impl RangeBounds<usize>,
should_remove: impl FnMut(&mut T) -> bool,
) -> impl Iterator<Item = T> {
retain::ExtractIf { retain: retain::Retain::new(self, range), should_remove }
}
pub fn resize_with(&mut self, len: usize, mut value_fn: impl FnMut() -> T) {
let (capacity_slice, old_len, mut set_len) = self.as_uninit_slice_with_length_setter();
match len.cmp(&old_len) {
cmp::Ordering::Equal => {}
cmp::Ordering::Less => {
unsafe { set_len.set_len(len) };
unsafe { slice_assume_init_drop(&mut capacity_slice[len..old_len]) };
}
cmp::Ordering::Greater if len <= capacity_slice.len() => {
unwind_safe_write_slice(&mut capacity_slice[old_len..len], |_| value_fn());
unsafe { set_len.set_len(len) };
}
cmp::Ordering::Greater => {
self.extend(iter::repeat_with(value_fn).take(len - old_len));
}
}
}
}
impl<T: Clone, const N: usize> WordVec<T, N> {
pub fn from_elem(elem: T, count: usize) -> Self {
if count <= N {
let mut data = [const { MaybeUninit::uninit() }; N];
unwind_safe_write_slice(&mut data[..count], |_| elem.clone());
Self(Inner { small: ManuallyDrop::new(Small::new(count, data)) })
} else {
let mut large = Large::new_empty(count);
large.extend_iter(iter::repeat_n(elem, count));
Self(Inner { large: ManuallyDrop::new(large) })
}
}
pub fn resize(&mut self, len: usize, value: T) { self.resize_with(len, || value.clone()); }
}
fn unwind_safe_write_slice<T>(
slice: &mut [MaybeUninit<T>],
mut write_elem: impl FnMut(usize) -> T,
) {
struct DropGuard<'a, T> {
slice: &'a mut [MaybeUninit<T>],
len: usize,
}
impl<T> Drop for DropGuard<'_, T> {
fn drop(&mut self) {
unsafe { slice_assume_init_drop(&mut self.slice[..self.len]) };
}
}
let mut guard = DropGuard { slice, len: 0 };
while guard.len < guard.slice.len() {
let value = write_elem(guard.len);
guard.slice[guard.len].write(value);
guard.len += 1;
}
mem::forget(guard);
}
fn resolve_range(range: impl RangeBounds<usize>, current_len: usize) -> (usize, usize) {
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).unwrap_or(current_len),
Bound::Excluded(&n) => n,
Bound::Unbounded => current_len,
}
.min(current_len);
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).unwrap_or(end),
Bound::Unbounded => 0,
}
.min(end);
(start, end)
}
pub struct LengthSetter<'a> {
inner: LengthSetterInner<'a>,
#[cfg(debug_assertions)]
capacity: usize,
}
enum LengthSetterInner<'a> {
Small(&'a mut u8),
Large(&'a mut usize),
}
impl LengthSetter<'_> {
pub unsafe fn set_len(&mut self, new_len: usize) {
#[cfg(debug_assertions)]
debug_assert!(new_len <= self.capacity, "guaranteed by the caller of the writer function.");
match self.inner {
LengthSetterInner::Small(ref mut marker) => {
**marker = (unsafe { u8::try_from(new_len).unwrap_unchecked() } << 1) | 1;
}
LengthSetterInner::Large(ref mut len) => **len = new_len,
}
}
pub unsafe fn inc_len(&mut self) {
match self.inner {
LengthSetterInner::Small(ref mut marker) => {
**marker = unsafe { marker.unchecked_add(2) };
}
LengthSetterInner::Large(ref mut len) => {
**len = unsafe { len.unchecked_add(1) };
}
}
}
}
fn shift_left_once<T>(slice: &mut [MaybeUninit<T>]) {
let moved_items = slice.len().checked_sub(1).expect("cannot shift empty slice");
let ptr = slice.as_mut_ptr();
unsafe {
ptr::copy(ptr.add(1), ptr, moved_items);
}
}
fn shift_right_once<T>(slice: &mut [MaybeUninit<T>]) {
let moved_items = slice.len().checked_sub(1).expect("cannot shift empty slice");
let ptr = slice.as_mut_ptr();
unsafe {
ptr::copy(ptr, ptr.add(1), moved_items);
}
}
impl<T, const LENGTH: usize, const N: usize> From<[T; LENGTH]> for WordVec<T, N> {
fn from(value: [T; LENGTH]) -> Self {
Inner::<T, N>::assert_generics();
if LENGTH <= N {
let mut data = [const { MaybeUninit::uninit() }; N];
for (dest, src) in iter::zip(&mut data[..LENGTH], value) {
dest.write(src);
}
Self(Inner {
small: ManuallyDrop::new(Small {
marker: u8::try_from(LENGTH << 1).expect("LENGTH * 2 <= N * 2 <= 254") | 1,
data,
}),
})
} else {
let mut value = ManuallyDrop::new(value);
let large = unsafe { Large::new(LENGTH, &mut *value) };
Self(Inner { large: ManuallyDrop::new(large) })
}
}
}
impl<T, const N: usize> Drop for WordVec<T, N> {
fn drop(&mut self) {
match self.0.parse_marker() {
ParsedMarker::Small(len) => {
let small = unsafe { &mut self.0.small };
let uninit_slice = unsafe { small.data.get_unchecked_mut(..usize::from(len)) };
unsafe { slice_assume_init_drop::<T>(uninit_slice) };
}
ParsedMarker::Large => {
let large = unsafe { &mut self.0.large };
let (allocated, data_start) = large.as_allocated_mut();
unsafe {
let slice_ptr = ptr::slice_from_raw_parts_mut(data_start, allocated.len);
slice_ptr.drop_in_place();
};
unsafe {
dealloc(large.0.as_ptr().cast(), large.current_layout());
}
}
}
}
}
impl<T, const N: usize> Clone for WordVec<T, N>
where
T: Clone,
{
fn clone(&self) -> Self { self.iter().cloned().collect() }
fn clone_from(&mut self, source: &Self) {
let source_len = source.len();
let dest_len = self.len();
let reused_len = source_len.min(dest_len);
for (dest, src) in iter::zip(&mut self[..reused_len], &source[..reused_len]) {
dest.clone_from(src);
}
if source_len > dest_len {
self.extend(source[reused_len..].iter().cloned());
} else if source_len < dest_len {
self.truncate(source.len());
}
}
}
impl<T, const N: usize> Extend<T> for WordVec<T, N> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
let mut iter = iter.into_iter();
let hint_min = iter.size_hint().0;
match self.0.parse_marker() {
ParsedMarker::Small(len) if usize::from(len) + hint_min <= N => {
unsafe {
self.extend_small_iter(iter.by_ref().take(N - usize::from(len)));
}
let mut peekable = iter.peekable();
if peekable.peek().is_some() {
unsafe {
self.move_small_to_large(N * 2);
}
unsafe { self.extend_large_iter(peekable) }
}
}
ParsedMarker::Small(len) => {
unsafe {
self.move_small_to_large(usize::from(len) + hint_min);
}
unsafe { self.extend_large_iter(iter) }
}
ParsedMarker::Large => {
unsafe { self.extend_large_iter(iter) }
}
}
}
}
unsafe impl<T: Send, const N: usize> Send for WordVec<T, N> {}
unsafe impl<T: Sync, const N: usize> Sync for WordVec<T, N> {}
mod macros;
mod trivial_traits;