turbocow 0.3.0-beta.2

Compact, clone-on-write vectors, strings, maps and sets with inline + referenced storage — a superset of ecow.
Documentation
use crate::allocator::AllocatorProvider;
use crate::dynamic::common::limit::LEN_VALUE_MASK_USIZE;
use crate::dynamic::types::{DynamicVec, Variant, VariantMut};
use crate::vec::types::EcoVec;

impl<'a, const N: usize, A> DynamicVec<'a, N, A>
where
    A: AllocatorProvider,
{
    #[inline]
    pub fn len(&self) -> usize {
        if self.is_pure_inline() {
            unsafe { (*self.0.inline).len() }
        } else {
            // Referenced and Spilled are both repr(C) with `len: usize`
            // at offset size_of::<usize>().  The mask strips Referenced
            // tag bits and is a no-op for Spilled (len ≤ isize::MAX).
            unsafe { (*self.0.referenced).len & LEN_VALUE_MASK_USIZE }
        }
    }

    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        if self.is_pure_inline() {
            unsafe { (*self.0.inline).as_slice() }
        } else {
            // Referenced and Spilled are both repr(C) with ptr at offset 0
            // and len at offset size_of::<usize>().  Reading through the
            // Referenced arm is sound for Spilled because the fields have
            // the same type and offset.  The mask strips Referenced tag
            // bits and is a no-op for Spilled (len ≤ isize::MAX).
            let r = unsafe { &*self.0.referenced };
            let len = r.len & LEN_VALUE_MASK_USIZE;
            unsafe { core::slice::from_raw_parts(r.ptr, len) }
        }
    }

    #[inline]
    pub fn make_mut(&mut self, size_hint: usize) -> &mut [u8]
    where
        A: Clone,
    {
        // Materialise Referenced inline — reads data directly from the
        // union member (one tag check, no separate helper function).
        // Borrows &A instead of cloning upfront; from_slice_in_owned
        // defers the clone until the inline/spill decision is made.
        if self.is_referenced() {
            let r = unsafe { &*self.0.referenced };
            let len = r.len & LEN_VALUE_MASK_USIZE;
            let ptr = r.ptr;
            // Safety: from_referenced guarantees ptr/len are valid.
            let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
            *self = Self::from_slice_in_owned(slice, &r.allocator, size_hint);
        }
        match self.variant_mut() {
            VariantMut::Inline(inline) => inline.as_mut_slice(),
            VariantMut::Spilled(spilled) => {
                // Ensure uniqueness via the u8-specialised memcpy path
                // instead of the generic element-by-element clone.
                // The subsequent make_mut() sees is_unique()=true and
                // returns the mutable slice without re-cloning.
                spilled.make_unique_bytes();
                spilled.make_mut()
            }
            VariantMut::Referenced(_) => unreachable!(),
        }
    }

    #[inline]
    pub fn push(&mut self, byte: u8)
    where
        A: Clone,
    {
        if self.is_referenced() {
            self.make_mut(1);
            // Fall through — now Inline or Spilled.
        }
        match self.variant_mut() {
            VariantMut::Inline(inline) => {
                if inline.push(byte).is_err() {
                    let alloc = inline.allocator.clone();
                    // NLL: inline borrow is dead after clone.
                    self.spill_push(byte, alloc);
                }
            }
            VariantMut::Spilled(spilled) => {
                spilled.push(byte);
            }
            // make_mut() above guarantees the variant is no longer Referenced.
            VariantMut::Referenced(_) => unreachable!(),
        }
    }

    #[inline]
    pub fn extend_from_slice(&mut self, bytes: &[u8])
    where
        A: Clone,
    {
        if self.is_referenced() {
            self.make_mut(bytes.len());
            // Fall through — now Inline or Spilled.
        }
        match self.variant_mut() {
            VariantMut::Inline(inline) => {
                if inline.extend_from_slice(bytes).is_err() {
                    let needed = inline.len() + bytes.len();
                    let alloc = inline.allocator.clone();
                    // NLL: inline borrow is dead after clone.
                    self.spill_extend(bytes, needed, alloc);
                }
            }
            VariantMut::Spilled(spilled) => {
                spilled.extend_from_byte_slice(bytes);
            }
            // make_mut() above guarantees the variant is no longer Referenced.
            VariantMut::Referenced(_) => unreachable!(),
        }
    }

    #[inline]
    pub fn clear(&mut self)
    where
        A: Clone,
    {
        if self.is_referenced() {
            // Skip materialisation — copying the borrowed data just to
            // discard it is wasteful.  Replace with a fresh empty inline.
            let alloc = unsafe { (*self.0.referenced).allocator.clone() };
            *self = Self::new_in(alloc);
            return;
        }
        match self.variant_mut() {
            VariantMut::Inline(inline) => inline.clear(),
            VariantMut::Spilled(spilled) => spilled.clear(),
            VariantMut::Referenced(_) => unreachable!(),
        }
    }

    #[inline]
    pub fn truncate(&mut self, target: usize)
    where
        A: Clone,
    {
        match self.variant_mut() {
            VariantMut::Referenced(r) => {
                let cur_len = r.len & LEN_VALUE_MASK_USIZE;
                if target < cur_len {
                    use crate::dynamic::common::limit::LEN_TAGS_USIZE;
                    r.len = target | LEN_TAGS_USIZE;
                }
            }
            VariantMut::Inline(inline) => inline.truncate(target),
            VariantMut::Spilled(spilled) => spilled.truncate(target),
        }
    }

    #[inline]
    #[allow(dead_code)]
    pub(crate) fn get_allocator(&self) -> &A
    where
        A: Clone,
    {
        // ZST allocators (e.g. Global) have no data — skip the dispatch.
        if core::mem::size_of::<A>() == 0 {
            unsafe { &*core::ptr::NonNull::<A>::dangling().as_ptr() }
        } else {
            match self.variant() {
                Variant::Referenced(r) => &r.allocator,
                Variant::Inline(inline) => inline.get_allocator(),
                Variant::Spilled(spilled) => spilled.get_allocator(),
            }
        }
    }

    /// Spill from inline to heap and push one byte.
    /// Allocator is passed directly from the match arm — no re-dispatch.
    #[cold]
    #[inline(never)]
    fn spill_push(&mut self, byte: u8, alloc: A)
    where
        A: Clone,
    {
        let mut eco = EcoVec::with_capacity_in(N * 2, alloc);
        // Safety: with_capacity_in(N * 2) guarantees capacity ≥ N + 1.
        // The current inline data is at most N bytes, plus 1 byte to push.
        // The fresh vec is unique (refcount = 1).
        // Read directly from the inline union member — this function is only
        // called from the Inline match arm, so skip the as_slice() dispatch.
        unsafe {
            eco.extend_from_byte_slice_unchecked((*self.0.inline).as_slice());
            eco.push_unchecked(byte);
            // Write EcoVec directly into *self — skip from_eco intermediate.
            self.emplace_eco(eco);
        }
    }

    /// Spill from inline to heap and extend with a byte slice.
    /// Allocator and needed capacity passed directly — no re-dispatch.
    #[cold]
    #[inline(never)]
    fn spill_extend(&mut self, bytes: &[u8], needed: usize, alloc: A)
    where
        A: Clone,
    {
        let mut eco = EcoVec::with_capacity_in(needed.next_power_of_two(), alloc);
        // Safety: capacity ≥ needed = self.len() + bytes.len().
        // The fresh vec is unique (refcount = 1).
        // Read directly from the inline union member — this function is only
        // called from the Inline match arm, so skip the as_slice() dispatch.
        unsafe {
            eco.extend_from_byte_slice_unchecked((*self.0.inline).as_slice());
            eco.extend_from_byte_slice_unchecked(bytes);
            // Write EcoVec directly into *self — skip from_eco intermediate.
            self.emplace_eco(eco);
        }
    }

    /// Write an EcoVec directly into `*self`, transitioning to the Spilled
    /// variant without an intermediate `MaybeUninit` or `from_eco` call.
    ///
    /// # Safety
    /// - The current variant must be Inline or Referenced (Drop for these
    ///   is a no-op — no heap resources to clean up).
    /// - The caller must have already copied any needed data from *self.
    #[inline(always)]
    unsafe fn emplace_eco(&mut self, eco: EcoVec<u8, A>) {
        use crate::dynamic::common::limit::TAG_OVERLAPS_LEN;
        use core::mem::ManuallyDrop;
        use core::ptr::addr_of_mut;

        let self_ptr = self as *mut Self;
        // On 64-bit LE, the high byte of EcoVec::len is at the tagged_len
        // position and is guaranteed 0 (len ≤ isize::MAX).  On other
        // layouts we must explicitly clear the tag byte.
        if !TAG_OVERLAPS_LEN {
            // Safety:
            // The tag byte lives at offset `N` within the inline union member,
            // which is in bounds of `*self`. Writing it does not touch any
            // initialized field the caller still needs (caller copied data out).
            unsafe {
                let tag_ptr = (addr_of_mut!((*self_ptr).0.inline) as *mut u8).add(N);
                tag_ptr.write(0);
            }
        }
        // Safety:
        // The current variant is Inline/Referenced (Drop is a no-op for these),
        // so overwriting the union with the Spilled member leaks nothing, and
        // `spilled_ptr` points to the correctly-aligned, in-bounds union slot.
        unsafe {
            let spilled_ptr = addr_of_mut!((*self_ptr).0.spilled);
            spilled_ptr.write(ManuallyDrop::new(eco));
        }
    }
}