use std::{
array,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ptr::addr_of,
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Mutex,
},
};
const CHUNK_SIZE: usize = 64;
type ChunkArray<T> = [MaybeUninit<T>; CHUNK_SIZE];
type Chunk<T> = Box<ChunkArray<T>>;
pub struct ChunkedVec<T> {
inner: Mutex<ChunkedVecInner<T>>,
len: AtomicUsize,
_marker: PhantomData<Vec<T>>,
}
struct ChunkedVecInner<T> {
chunks: Vec<Chunk<T>>,
}
pub struct Iter<'a, T> {
chunked_vec: &'a ChunkedVec<T>,
next_index: usize,
next_ptr: Option<*const MaybeUninit<T>>,
}
pub struct IterMut<'a, T> {
chunked_vec: &'a ChunkedVec<T>,
next_index: usize,
next_ptr: Option<*mut MaybeUninit<T>>,
}
pub struct IntoIter<T> {
inner: ChunkedVecInner<T>,
len: AtomicUsize,
current: usize,
}
impl<T> ChunkedVecInner<T> {
fn index_ptr(&mut self, index: usize) -> *mut MaybeUninit<T> {
unsafe {
assert!(
index / CHUNK_SIZE < self.chunks.len(),
"index greater than capacity"
);
let chunk: *mut ChunkArray<T> = self
.chunks
.as_mut_ptr()
.add(index / CHUNK_SIZE)
.cast::<*mut ChunkArray<T>>()
.read();
chunk.cast::<MaybeUninit<T>>().add(index % CHUNK_SIZE)
}
}
}
impl<T> ChunkedVec<T> {
pub const fn new() -> Self {
Self {
inner: Mutex::new(ChunkedVecInner { chunks: Vec::new() }),
len: AtomicUsize::new(0),
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.len.load(SeqCst)
}
pub fn push(&self, value: T) {
unsafe {
let mut inner = self.inner.lock().unwrap();
if self.len() == inner.chunks.len() * CHUNK_SIZE {
inner
.chunks
.push(Box::new(array::from_fn(|_| MaybeUninit::uninit())));
}
let new_element: *mut MaybeUninit<T> = inner.index_ptr(self.len());
new_element.write(MaybeUninit::new(value));
self.len.fetch_add(1, SeqCst);
}
}
pub unsafe fn index(&self, index: usize) -> &T {
assert!(index < self.len(), "index out of bounds");
let mut inner = self.inner.lock().unwrap();
let ptr: *mut MaybeUninit<T> = inner.index_ptr(index);
(*ptr).as_ptr().as_ref().unwrap()
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn index_mut(&self, index: usize) -> &mut T {
assert!(index < self.len(), "index out of bounds");
let mut inner = self.inner.lock().unwrap();
let ptr: *mut MaybeUninit<T> = inner.index_ptr(index);
(*ptr).as_mut_ptr().as_mut().unwrap()
}
pub unsafe fn iter(&self) -> Iter<'_, T> {
Iter {
chunked_vec: self,
next_index: 0,
next_ptr: None,
}
}
pub unsafe fn iter_mut(&self) -> IterMut<'_, T> {
IterMut {
chunked_vec: self,
next_index: 0,
next_ptr: None,
}
}
}
impl<T> IntoIterator for ChunkedVec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> {
let self_manually_drop = ManuallyDrop::new(self);
let mutex = unsafe { addr_of!(self_manually_drop.inner).read() };
let inner = mutex.into_inner().unwrap();
unsafe {
IntoIter {
inner,
len: addr_of!(self_manually_drop.len).read(),
current: 0,
}
}
}
}
impl<T> Drop for ChunkedVec<T> {
fn drop(&mut self) {
unsafe {
let inner: &mut ChunkedVecInner<T> = &mut self.inner.lock().unwrap();
for i in 0..self.len() {
(*inner.index_ptr(i)).assume_init_drop();
}
}
}
}
impl<T> Drop for IntoIter<T> {
fn drop(&mut self) {
unsafe {
for i in self.current..self.len.load(SeqCst) {
(*self.inner.index_ptr(i)).assume_init_drop();
}
}
}
}
impl<'a, T> Iter<'a, T> {
pub fn next_index(&self) -> usize {
self.next_index
}
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.next_index < self.chunked_vec.len() {
let current_ptr = self.next_ptr.unwrap_or_else(|| {
self.chunked_vec
.inner
.lock()
.unwrap()
.index_ptr(self.next_index)
});
self.next_index += 1;
self.next_ptr = if self.next_index % CHUNK_SIZE == 0 {
None
} else {
unsafe { Some(current_ptr.add(1)) }
};
Some(unsafe { &*current_ptr.cast::<T>() })
} else {
None
}
}
}
impl<'a, T> IterMut<'a, T> {
pub fn next_index(&self) -> usize {
self.next_index
}
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<&'a mut T> {
if self.next_index < self.chunked_vec.len() {
let current_ptr = self.next_ptr.unwrap_or_else(|| {
self.chunked_vec
.inner
.lock()
.unwrap()
.index_ptr(self.next_index)
});
self.next_index += 1;
self.next_ptr = if self.next_index % CHUNK_SIZE == 0 {
None
} else {
unsafe { Some(current_ptr.add(1)) }
};
Some(unsafe { &mut *current_ptr.cast::<T>() })
} else {
None
}
}
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.current < self.len.load(SeqCst) {
let ptr = self.inner.index_ptr(self.current);
self.current += 1;
Some(unsafe { ptr.read().assume_init() })
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chunked_vec_basic() {
let vec = ChunkedVec::new();
vec.push(1);
vec.push(2);
vec.push(3);
assert_eq!(vec.len(), 3);
let ref0 = unsafe { vec.index(0) };
let ref1 = unsafe { vec.index(1) };
let ref2 = unsafe { vec.index(2) };
assert_eq!(*ref0, 1);
assert_eq!(*ref1, 2);
assert_eq!(*ref2, 3);
assert_eq!(*ref0, 1);
assert_eq!(*ref1, 2);
assert_eq!(*ref2, 3);
let ref0_mut = unsafe { vec.index_mut(0) };
*ref0_mut = 4;
assert_eq!(*ref0_mut, 4);
assert_eq!(*ref1, 2);
assert_eq!(*ref2, 3);
}
#[test]
fn test_chunked_vec_large() {
let vec = ChunkedVec::new();
for i in 0..1000 {
vec.push(i);
}
assert_eq!(vec.len(), 1000);
let mut refs_vec = Vec::new();
for i in 0..1000 {
let ref_i = unsafe { vec.index(i) };
assert_eq!(*ref_i, i);
refs_vec.push(ref_i);
}
for i in 1000..2000 {
vec.push(i);
}
assert_eq!(vec.len(), 2000);
for i in 0..1000 {
assert_eq!(*refs_vec[i], i);
}
}
#[test]
fn test_iter() {
let vec = ChunkedVec::new();
for i in 0..1000 {
vec.push(i);
}
let mut iter = unsafe { vec.iter() };
for i in 0..1000 {
assert_eq!(iter.next(), Some(&i));
}
assert_eq!(iter.next(), None);
}
#[test]
fn test_iter_mut() {
let vec = ChunkedVec::new();
for i in 0..1000 {
vec.push(i);
}
let mut iter = unsafe { vec.iter_mut() };
for i in 0..1000 {
assert_eq!(iter.next(), Some(&mut { i }));
}
assert_eq!(iter.next(), None);
}
#[test]
fn test_iter_mut_always_near_end() {
let vec = ChunkedVec::new();
let mut iter = unsafe { vec.iter_mut() };
for i in 0..1000 {
vec.push(i);
assert_eq!(iter.next(), Some(&mut { i }));
}
assert_eq!(iter.next(), None);
}
#[test]
fn test_into_iter() {
let vec = ChunkedVec::new();
for i in 0..1000 {
vec.push(i);
}
let mut iter = vec.into_iter();
for i in 0..1000 {
assert_eq!(iter.next(), Some(i));
}
assert_eq!(iter.next(), None);
let vec = ChunkedVec::new();
for i in 0..1000 {
vec.push(i);
}
let mut iter = vec.into_iter();
for i in 0..500 {
assert_eq!(iter.next(), Some(i));
}
}
}