I needed something to allow me to track progress of concurrent work loads, and so I conceived of this data structure:
pub struct Oncewrite<T> {
data: UnsafeCell<Vec<Option<T>>>,
length: AtomicU64,
}
unsafe impl<T: Sync> Sync for Oncewrite<T> {}
impl<T: Default + Clone> Oncewrite<T> {
pub fn new(size: usize) -> Self {
Oncewrite {
data: UnsafeCell::new(vec![None; size]),
length: AtomicU64::new(0 as u64),
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
let raw = self.data.get();
unsafe { (*raw).iter().filter_map(|x| x.as_ref()) }
}
pub fn insert(&self, index: usize, value: T) -> anyhow::Result<()> {
let raw = self.data.get();
unsafe {
if (*raw).len() <= index {
return Err(anyhow::anyhow!("Index out of bounds"));
};
if (*raw).get_unchecked(index).is_some() {
return Err(anyhow::anyhow!("Value already set"));
};
(*raw).get_unchecked_mut(index).replace(value);
self.length.fetch_add(1, Ordering::Relaxed);
}
Ok(())
}
pub fn get(&self, index: usize) -> anyhow::Result<&T> {
let raw = self.data.get();
unsafe {
if (*raw).len() <= index {
return Err(anyhow::anyhow!("Index out of bounds"));
};
if (*raw).get_unchecked(index).is_none() {
return Err(anyhow::anyhow!("Value not set"));
};
Ok((*raw).get_unchecked(index).as_ref().unwrap())
}
}
pub fn get_length(&self) -> usize {
self.length.load(Ordering::Relaxed) as usize
}
}
I've done some basic testing and it should work and be sound. Any feedback? Is there already a well-known solution that already does this?