use std::collections::TryReserveError;
use std::sync::Arc;
pub(super) const CHUNK: usize = 512;
const SHIFT: u32 = CHUNK.trailing_zeros();
const MASK: usize = CHUNK - 1;
pub(super) const FANOUT: usize = 128;
const FAN_SHIFT: u32 = FANOUT.trailing_zeros();
const FAN_MASK: usize = FANOUT - 1;
pub(super) const SPAN: usize = CHUNK * FANOUT;
const SPAN_SHIFT: u32 = SHIFT + FAN_SHIFT;
const SPAN_MASK: usize = SPAN - 1;
const _: () = assert!(CHUNK.is_power_of_two() && FANOUT.is_power_of_two());
type Leaf<T> = Arc<Vec<T>>;
type Interior<T> = Arc<[Leaf<T>]>;
pub(super) struct ChunkedVec<T> {
root: Arc<[Interior<T>]>,
len: usize,
}
impl<T> Default for ChunkedVec<T> {
fn default() -> Self {
Self {
root: Arc::new([]),
len: 0,
}
}
}
fn appended<X: Clone>(slice: &[X], last: X) -> Arc<[X]> {
slice.iter().cloned().chain(std::iter::once(last)).collect()
}
fn truncated<X: Clone>(slice: &[X]) -> Arc<[X]> {
slice[..slice.len() - 1].iter().cloned().collect()
}
impl<T> Clone for ChunkedVec<T> {
fn clone(&self) -> Self {
Self {
root: Arc::clone(&self.root),
len: self.len,
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for ChunkedVec<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T: PartialEq> PartialEq for ChunkedVec<T> {
fn eq(&self, other: &Self) -> bool {
self.len == other.len && self.iter().eq(other.iter())
}
}
impl<T> ChunkedVec<T> {
pub(super) fn new() -> Self {
Self::default()
}
#[inline]
pub(super) fn len(&self) -> usize {
self.len
}
#[inline]
pub(super) fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub(super) fn get(&self, i: usize) -> Option<&T> {
if i >= self.len {
return None;
}
unsafe {
let node = self.root.get_unchecked(i >> SPAN_SHIFT);
let leaf = node.get_unchecked((i >> SHIFT) & FAN_MASK);
Some(leaf.get_unchecked(i & MASK))
}
}
#[inline]
pub(super) fn chunk_from(&self, i: usize) -> &[T] {
if i >= self.len {
return &[];
}
let leaf = unsafe {
let node = self.root.get_unchecked(i >> SPAN_SHIFT);
node.get_unchecked((i >> SHIFT) & FAN_MASK)
};
&leaf[i & MASK..]
}
fn leaves(&self) -> impl DoubleEndedIterator<Item = &Vec<T>> + '_ {
self.root
.iter()
.flat_map(|node| node.iter())
.map(|leaf| &**leaf)
}
pub(super) fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + '_ {
self.leaves().flat_map(|leaf| leaf.iter())
}
pub(super) fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
let mut out = Vec::with_capacity(self.len);
for leaf in self.leaves() {
out.extend_from_slice(leaf);
}
out
}
pub(super) fn capacity(&self) -> usize {
self.leaves().map(|leaf| leaf.capacity()).sum()
}
pub(super) fn chunk_count(&self) -> usize {
self.root.iter().map(|node| node.len()).sum()
}
pub(super) fn tree_overhead_bytes(&self) -> usize {
const ARC_HEADER: usize = 2 * size_of::<usize>();
ARC_HEADER + self.root.len() * (size_of::<Interior<T>>() + ARC_HEADER)
}
}
fn capacity_overflow() -> TryReserveError {
Vec::<u8>::new()
.try_reserve_exact(usize::MAX)
.expect_err("usize::MAX bytes exceeds isize::MAX")
}
impl<T: Clone> ChunkedVec<T> {
pub(super) fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
let bytes = self
.len
.checked_add(additional)
.and_then(|total| total.checked_mul(size_of::<T>().max(1)));
if bytes.is_none_or(|bytes| bytes > isize::MAX as usize) {
return Err(capacity_overflow());
}
Ok(())
}
#[inline]
pub(super) fn get_mut(&mut self, i: usize) -> Option<&mut T> {
if i >= self.len {
return None;
}
let node = &mut Arc::make_mut(&mut self.root)[i >> SPAN_SHIFT];
let leaf = &mut Arc::make_mut(node)[(i >> SHIFT) & FAN_MASK];
Some(&mut Arc::make_mut(leaf)[i & MASK])
}
pub(super) fn push(&mut self, value: T) {
let len = self.len;
if len & MASK == 0 {
let capacity = if len == 0 { 0 } else { CHUNK };
let leaf: Leaf<T> = Arc::new(Vec::with_capacity(capacity));
if len & SPAN_MASK == 0 {
self.root = appended(&self.root, Arc::new([leaf]));
} else {
let root = Arc::make_mut(&mut self.root);
let node = root.last_mut().expect("len > 0");
*node = appended(node, leaf);
}
}
let root = Arc::make_mut(&mut self.root);
let node = Arc::make_mut(root.last_mut().expect("node exists"));
Arc::make_mut(node.last_mut().expect("leaf exists")).push(value);
self.len += 1;
}
pub(super) fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
let root = Arc::make_mut(&mut self.root);
let node = root.last_mut().expect("len > 0");
let leaf = Arc::make_mut(node).last_mut().expect("len > 0");
let value = Arc::make_mut(leaf).pop();
if leaf.is_empty() {
if node.len() == 1 {
self.root = truncated(&self.root);
} else {
*node = truncated(node);
}
}
self.len -= 1;
value
}
pub(super) fn resize_with(&mut self, new_len: usize, mut fill: impl FnMut() -> T) {
while self.len > new_len {
self.pop();
}
while self.len < new_len {
self.push(fill());
}
}
pub(super) fn swap_remove(&mut self, i: usize) -> T {
assert!(i < self.len, "swap_remove index {i} out of bounds");
let last = self.pop().expect("non-empty");
if i == self.len {
return last;
}
std::mem::replace(self.get_mut(i).expect("in bounds"), last)
}
}
impl<T> std::ops::Index<usize> for ChunkedVec<T> {
type Output = T;
#[inline]
fn index(&self, i: usize) -> &T {
self.get(i).expect("ChunkedVec index out of bounds")
}
}
impl<T: Clone> std::ops::IndexMut<usize> for ChunkedVec<T> {
#[inline]
fn index_mut(&mut self, i: usize) -> &mut T {
self.get_mut(i).expect("ChunkedVec index out of bounds")
}
}
impl<T: Clone> FromIterator<T> for ChunkedVec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut out = Self::new();
for value in iter {
out.push(value);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
impl<T> ChunkedVec<T> {
fn node(&self, n: usize) -> &Interior<T> {
&self.root[n]
}
fn leaf(&self, i: usize) -> &Leaf<T> {
&self.root[i >> SPAN_SHIFT][(i >> SHIFT) & FAN_MASK]
}
}
#[test]
fn behaves_like_a_vec_across_chunk_boundaries() {
let mut v: ChunkedVec<usize> = ChunkedVec::new();
let mut reference = Vec::new();
for i in 0..(CHUNK * 3 + 7) {
v.push(i);
reference.push(i);
}
assert_eq!(v.len(), reference.len());
assert!(v.iter().eq(reference.iter()));
assert_eq!(v.swap_remove(3), reference.swap_remove(3));
assert_eq!(v.swap_remove(CHUNK + 1), reference.swap_remove(CHUNK + 1));
assert_eq!(
v.swap_remove(v.len() - 1),
reference.swap_remove(reference.len() - 1)
);
assert!(v.iter().eq(reference.iter()));
assert_eq!(v.to_vec(), reference);
v.resize_with(10, || 0);
reference.truncate(10);
assert!(v.iter().eq(reference.iter()));
v.resize_with(CHUNK + 2, || 9);
reference.resize(CHUNK + 2, 9);
assert!(v.iter().eq(reference.iter()));
assert_eq!(v.get(v.len() - 1), reference.last());
assert_eq!(v.to_vec(), reference);
assert_eq!(ChunkedVec::<usize>::new().to_vec(), Vec::<usize>::new());
}
#[test]
fn behaves_like_a_vec_across_interior_boundaries() {
let n = SPAN * 2 + CHUNK + 3;
let mut v: ChunkedVec<u32> = (0..n as u32).collect();
let mut reference: Vec<u32> = (0..n as u32).collect();
assert_eq!(v.len(), n);
assert_eq!(v.chunk_count(), n.div_ceil(CHUNK));
assert!(v.iter().eq(reference.iter()));
assert!(v.iter().rev().eq(reference.iter().rev()));
for i in [
0,
CHUNK - 1,
CHUNK,
SPAN - 1,
SPAN,
SPAN + 1,
2 * SPAN,
n - 1,
] {
assert_eq!(v.get(i), reference.get(i), "index {i}");
assert_eq!(v[i], i as u32);
}
assert_eq!(v.get(n), None);
v.resize_with(SPAN - 1, || 0);
reference.truncate(SPAN - 1);
assert_eq!(v.root.len(), 1, "the emptied interior nodes are dropped");
assert!(v.iter().eq(reference.iter()));
v.resize_with(SPAN + 5, || 7);
reference.resize(SPAN + 5, 7);
assert!(v.iter().eq(reference.iter()));
assert_eq!(v.to_vec(), reference);
assert_eq!(v.swap_remove(1), reference.swap_remove(1));
assert_eq!(v.swap_remove(SPAN), reference.swap_remove(SPAN));
assert!(v.iter().eq(reference.iter()));
while let Some(x) = v.pop() {
assert_eq!(Some(x), reference.pop());
}
assert!(reference.is_empty());
assert!(v.is_empty());
assert!(v.root.is_empty());
v.push(1);
assert_eq!(v.to_vec(), vec![1]);
}
#[test]
fn clones_share_chunks_until_written() {
let mut a: ChunkedVec<u64> = (0..(CHUNK as u64 * 4)).collect();
let b = a.clone();
assert!(Arc::ptr_eq(&a.root, &b.root), "a clone shares the root");
a[5] = 999;
assert_eq!(b[5], 5, "the clone is unaffected by writes");
assert_eq!(a[5], 999);
assert!(Arc::ptr_eq(a.leaf(CHUNK), b.leaf(CHUNK)));
assert!(!Arc::ptr_eq(a.leaf(0), b.leaf(0)));
let leaf = Arc::as_ptr(a.leaf(0));
a[6] = 1000;
assert_eq!(Arc::as_ptr(a.leaf(0)), leaf);
}
#[test]
fn clones_are_independent_across_interior_boundaries() {
let n = SPAN * 3 + 17;
let original: ChunkedVec<u64> = (0..n as u64).collect();
let reference: Vec<u64> = (0..n as u64).collect();
let points = [0, CHUNK - 1, CHUNK, SPAN - 1, SPAN, 2 * SPAN + CHUNK, n - 1];
let mut copy = original.clone();
for &i in &points {
copy[i] = u64::MAX - i as u64;
}
assert!(original.iter().eq(reference.iter()));
for &i in &points {
assert_eq!(copy[i], u64::MAX - i as u64);
}
assert!(Arc::ptr_eq(
original.leaf(SPAN + CHUNK),
copy.leaf(SPAN + CHUNK)
));
assert!(!Arc::ptr_eq(original.node(1), copy.node(1)));
assert!(Arc::ptr_eq(
original.leaf(SPAN + 2 * CHUNK),
copy.leaf(SPAN + 2 * CHUNK)
));
let mut original = original;
let snapshot = original.clone();
for &i in &points {
original[i] = 42;
}
assert!(snapshot.iter().eq(reference.iter()));
let mut grown = snapshot.clone();
for i in 0..(SPAN + CHUNK) as u64 {
grown.push(n as u64 + i);
}
assert_eq!(snapshot.len(), n);
assert!(snapshot.iter().eq(reference.iter()));
assert!(grown.iter().take(n).eq(reference.iter()));
assert_eq!(grown[n + SPAN], (n + SPAN) as u64);
let mut shrunk = snapshot.clone();
shrunk.resize_with(SPAN - 3, || 0);
assert_eq!(shrunk.len(), SPAN - 3);
assert!(snapshot.iter().eq(reference.iter()));
assert_eq!(shrunk.swap_remove(0), 0);
assert_eq!(shrunk[0], (SPAN - 4) as u64);
assert_eq!(snapshot[0], 0);
assert_eq!(snapshot[SPAN - 4], (SPAN - 4) as u64);
}
#[test]
fn small_vectors_stay_small() {
let v: ChunkedVec<u64> = (0..3).collect();
assert!(v.capacity() < CHUNK);
assert_eq!(v.root.len(), 1);
assert_eq!(v.node(0).len(), 1);
assert_eq!(v.chunk_count(), 1);
}
#[test]
fn unrepresentable_reservations_fail() {
let mut v: ChunkedVec<u64> = ChunkedVec::new();
assert!(v.try_reserve_exact(1 << 60).is_err());
assert!(v.try_reserve_exact(usize::MAX).is_err());
assert!(v.try_reserve_exact(SPAN * 3).is_ok());
assert!(v.is_empty());
}
}