use std::error::Error;
use std::fmt::Display;
use std::marker::PhantomData;
pub struct VecWriter<'vec, T> {
storage: &'vec mut Vec<T>,
taken: usize,
}
pub struct Shard<'vec, T> {
storage: *mut T,
start_offset: usize,
end_offset: usize,
initialised_up_to: usize,
_phantom: PhantomData<&'vec mut T>,
}
impl<T> Drop for Shard<'_, T> {
fn drop(&mut self) {
for offset in self.start_offset..self.initialised_up_to {
unsafe { self.storage.add(offset).read() };
}
}
}
unsafe impl<T: Send> Send for Shard<'_, T> {}
unsafe impl<T: Sync> Sync for Shard<'_, T> {}
impl<'vec, T> VecWriter<'vec, T> {
pub fn new(storage: &'vec mut Vec<T>) -> Self {
let taken = storage.len();
Self { storage, taken }
}
pub fn take_shard(&mut self, n: usize) -> Shard<'vec, T> {
self.try_take_shard(n).unwrap_or_else(|| {
panic!(
"Tried to take {n} when only {} available",
self.storage.capacity() - self.taken
);
})
}
pub fn try_take_shard(&mut self, n: usize) -> Option<Shard<'vec, T>> {
let end_offset = self.taken.saturating_add(n);
if end_offset > self.storage.capacity() {
return None;
}
let shard = Shard {
storage: self.storage.as_mut_ptr(),
start_offset: self.taken,
initialised_up_to: self.taken,
end_offset,
_phantom: Default::default(),
};
self.taken = end_offset;
Some(shard)
}
#[track_caller]
pub fn return_shard(&mut self, shard: Shard<T>) {
self.try_return_shard(shard).unwrap()
}
pub fn try_return_shard(&mut self, shard: Shard<T>) -> Result<(), InitError> {
if self.storage.as_mut_ptr() != shard.storage {
return Err(InitError::WrongVec);
}
if shard.initialised_up_to != shard.end_offset {
return Err(InitError::UninitElements);
}
if self.storage.len() != shard.start_offset {
return Err(InitError::OutOfOrder);
}
unsafe { self.storage.set_len(shard.initialised_up_to) };
core::mem::forget(shard);
Ok(())
}
}
impl<T> Shard<'_, T> {
#[track_caller]
#[inline]
pub fn push(&mut self, value: T) {
self.try_push(value).unwrap();
}
#[inline]
pub fn try_push(&mut self, value: T) -> Result<(), InsufficientCapacity> {
if self.initialised_up_to == self.end_offset {
return Err(InsufficientCapacity);
}
unsafe { self.storage.add(self.initialised_up_to).write(value) };
self.initialised_up_to += 1;
Ok(())
}
#[inline]
pub fn len(&self) -> usize {
self.end_offset - self.start_offset
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn init_mut(&mut self) -> &mut [T] {
unsafe {
std::slice::from_raw_parts_mut(
self.storage.add(self.start_offset),
self.initialised_up_to - self.start_offset,
)
}
}
#[inline]
pub fn output_offset(&self) -> usize {
self.initialised_up_to
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct InsufficientCapacity;
impl Error for InsufficientCapacity {}
impl Display for InsufficientCapacity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Insufficient capacity")
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum InitError {
UninitElements,
WrongVec,
OutOfOrder,
}
impl Error for InitError {}
impl Display for InitError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InitError::UninitElements => write!(f, "Elements not initialised"),
InitError::WrongVec => write!(f, "Shard returned to wrong vec"),
InitError::OutOfOrder => write!(f, "Shards returned out-of-order"),
}
}
}