use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::rustc_index::Idx;
const NUM_CHUNKS: usize = 32;
const FIRST_CHUNK: usize = 8;
#[inline]
fn chunk_of(index: usize) -> (usize, usize) {
let scaled = index / FIRST_CHUNK + 1;
let chunk = (usize::BITS - 1 - scaled.leading_zeros()) as usize;
let prior = FIRST_CHUNK * ((1usize << chunk) - 1);
(chunk, index - prior)
}
#[inline]
fn chunk_len(chunk: usize) -> usize {
FIRST_CHUNK << chunk
}
fn alloc_chunk<T>(len: usize) -> *mut MaybeUninit<T> {
let mut v: Vec<MaybeUninit<T>> = Vec::with_capacity(len);
v.resize_with(len, MaybeUninit::uninit);
Box::into_raw(v.into_boxed_slice()).cast::<MaybeUninit<T>>()
}
pub struct LockFreeAppendOnlyVec<T: Copy> {
chunks: [AtomicPtr<MaybeUninit<T>>; NUM_CHUNKS],
len: AtomicUsize,
writers: parking_lot::Mutex<()>,
}
unsafe impl<T: Copy + Send> Send for LockFreeAppendOnlyVec<T> {}
unsafe impl<T: Copy + Send> Sync for LockFreeAppendOnlyVec<T> {}
impl<T: Copy> Default for LockFreeAppendOnlyVec<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Copy> LockFreeAppendOnlyVec<T> {
#[allow(clippy::declare_interior_mutable_const)]
const NULL: AtomicPtr<MaybeUninit<T>> = AtomicPtr::new(ptr::null_mut());
pub const fn new() -> Self {
Self {
chunks: [Self::NULL; NUM_CHUNKS],
len: AtomicUsize::new(0),
writers: parking_lot::Mutex::new(()),
}
}
pub fn push(&self, val: T) -> usize {
let _writer = self.writers.lock();
let len = self.len.load(Ordering::Relaxed);
let (chunk, offset) = chunk_of(len);
assert!(chunk < NUM_CHUNKS, "LockFreeAppendOnlyVec grew past {NUM_CHUNKS} chunks");
let mut ptr = self.chunks[chunk].load(Ordering::Relaxed);
if ptr.is_null() {
ptr = alloc_chunk::<T>(chunk_len(chunk));
self.chunks[chunk].store(ptr, Ordering::Relaxed);
}
unsafe {
ptr.add(offset).write(MaybeUninit::new(val));
}
self.len.store(len + 1, Ordering::Release);
len
}
pub fn get(&self, index: usize) -> Option<T> {
if index >= self.len.load(Ordering::Acquire) {
return None;
}
let (chunk, offset) = chunk_of(index);
let ptr = self.chunks[chunk].load(Ordering::Relaxed);
Some(unsafe { (*ptr.add(offset)).assume_init() })
}
}
impl<T: Copy> Drop for LockFreeAppendOnlyVec<T> {
fn drop(&mut self) {
for chunk in 0..NUM_CHUNKS {
let ptr = *self.chunks[chunk].get_mut();
if ptr.is_null() {
break;
}
unsafe {
drop(Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, chunk_len(chunk))));
}
}
}
}
#[derive(Default)]
pub struct AppendOnlyIndexVec<I: Idx, T: Copy> {
vec: LockFreeAppendOnlyVec<T>,
_marker: PhantomData<fn(&I)>,
}
impl<I: Idx, T: Copy> AppendOnlyIndexVec<I, T> {
pub fn new() -> Self {
Self { vec: LockFreeAppendOnlyVec::new(), _marker: PhantomData }
}
pub fn push(&self, val: T) -> I {
let i = self.vec.push(val);
I::new(i)
}
pub fn get(&self, i: I) -> Option<T> {
let i = i.index();
self.vec.get(i)
}
}
#[derive(Default)]
pub struct AppendOnlyVec<T: Copy> {
vec: parking_lot::RwLock<Vec<T>>,
}
impl<T: Copy> AppendOnlyVec<T> {
pub fn new() -> Self {
Self { vec: Default::default() }
}
pub fn push(&self, val: T) -> usize {
let mut v = self.vec.write();
let n = v.len();
v.push(val);
n
}
pub fn get(&self, i: usize) -> Option<T> {
self.vec.read().get(i).copied()
}
pub fn iter_enumerated(&self) -> impl Iterator<Item = (usize, T)> {
(0..).map_while(|i| Some((i, self.get(i)?)))
}
pub fn iter(&self) -> impl Iterator<Item = T> {
(0..).map_while(|i| self.get(i))
}
}
impl<T: Copy + PartialEq> AppendOnlyVec<T> {
pub fn contains(&self, val: T) -> bool {
self.iter().any(|v| v == val)
}
}
impl<A: Copy> FromIterator<A> for AppendOnlyVec<A> {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let this = Self::new();
for val in iter {
this.push(val);
}
this
}
}