stele 0.2.1

An atomic Vec-like structure with no copying on allocation
Documentation
use std::{
    alloc::{Allocator, Global},
    sync::atomic::Ordering,
};

use crate::{max_len, split_idx, sync::Arc, ReadHandle, Stele};

/// A `WriteHandle` for a [`Stele`].
/// 
/// This must be `!Sync` because while you can safely reserve a slot to avoid write-write conflicts
/// in any one memory location, there can still be a race where a concurrent push while a previous
/// push is still allocating will segfault, necessitating the seperate load and store of capacity in
/// [`push`](WriteHandle::push())
#[derive(Debug)]
pub struct WriteHandle<T, A: Allocator = Global> {
    pub(crate) handle: Arc<Stele<T, A>>,
}

unsafe impl<T, A: Allocator> Send for WriteHandle<T, A> where T: Send + Sync {}
impl<T, A: Allocator> !Sync for WriteHandle<T, A> {}

impl<T, A: Allocator> WriteHandle<T, A> {
    /// Pushes a new item on to the end of the [`Stele`], allocating a new block of memory if necessary
    pub fn push(&self, val: T) {
        let idx = self.handle.cap.load(Ordering::Acquire);
        let (outer_idx, inner_idx) = split_idx(idx);
        unsafe {
            if idx.is_power_of_two() || idx == 0 {
                self.allocate(outer_idx, max_len(outer_idx));
            }
            *self.handle.inners[outer_idx]
                .load(Ordering::Acquire)
                .add(inner_idx) = crate::Inner::init(val);
        }
        self.handle.cap.store(idx + 1, Ordering::Release);
    }

    fn allocate(&self, idx: usize, len: usize) {
        self.handle.inners[idx]
            .compare_exchange(
                std::ptr::null_mut(),
                unsafe { crate::alloc_inner(&self.handle.allocator, len) },
                Ordering::AcqRel,
                Ordering::Relaxed,
            )
            .expect("The pointer is null because we have just incremented the cap to the head of this pointer");
    }

    /// Creates a new [`ReadHandle`]
    pub fn get_read_handle(&self) -> ReadHandle<T, A> {
        ReadHandle {
            handle: Arc::clone(&self.handle),
        }
    }

    /// Reads the value at the given index
    /// 
    /// # Panic
    /// 
    /// This function panics in debug if the given index is out of bounds
    pub fn read(&self, idx: usize) -> &T {
        self.handle.read(idx)
    }

    /// Attempts to read the value at the index and returns [`Some`] if the value exists, and [`None`]
    /// otherwise
    pub fn try_read(&self, idx: usize) -> Option<&T> {
        self.handle.try_read(idx)
    }

    /// Returns the current length of the underlying [`Stele`]
    /// 
    /// Note:
    /// By calling this through the [`WriteHandle`], you hold the only handle that can change the
    /// length and therefore this information is accurate until the next call to [`push`](WriteHandle::push)
    pub fn len(&self) -> usize {
        self.handle.len()
    }

    /// Returns if the underlying [`Stele`] is empty
    /// 
    /// Note:
    /// By calling this through the [`WriteHandle`], you hold the only handle that can change the
    /// length and therefore this information is accurate until the first call to [`push`](WriteHandle::push) if it
    /// returned `true`, and will remain accurate again after that as a [`Stele`] cannot remove elements
    pub fn is_empty(&self) -> bool {
        self.handle.is_empty()
    }
}

impl<T: Copy, A: Allocator> WriteHandle<T, A> {
    /// Get provides a way to get an owned copy of a value inside a [`Stele`]
    /// provided the `T` implements [`Copy`]
    /// 
    /// # Panic
    /// 
    /// This function panics in debug if the given index is out of bounds
    pub fn get(&self, idx: usize) -> T {
        self.handle.get(idx)
    }
}