summaryrefslogtreecommitdiff
path: root/crates/tinywasm/src/boxvec.rs
blob: bf7cd8b31c728cd3cda5d37fa8ae16143dc8ce26 (plain)
use crate::unlikely;
use alloc::{borrow::Cow, boxed::Box, vec};
use core::ops::RangeBounds;

// A Vec-like type that doesn't deallocate memory when popping elements.
#[derive(Debug)]
pub(crate) struct BoxVec<T> {
    pub(crate) data: Box<[T]>,
    pub(crate) end: usize,
}

impl<T: Copy + Default> BoxVec<T> {
    #[inline(always)]
    pub(crate) fn with_capacity(capacity: usize) -> Self {
        Self { data: vec![T::default(); capacity].into_boxed_slice(), end: 0 }
    }

    #[inline(always)]
    pub(crate) fn push(&mut self, value: T) {
        assert!(self.end <= self.data.len(), "stack overflow");
        self.data[self.end] = value;
        self.end += 1;
    }

    #[inline(always)]
    pub(crate) fn pop(&mut self) -> Option<T> {
        assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
        if unlikely(self.end == 0) {
            None
        } else {
            self.end -= 1;
            Some(self.data[self.end])
        }
    }

    #[inline(always)]
    pub(crate) fn len(&self) -> usize {
        self.end
    }

    #[inline(always)]
    pub(crate) fn extend_from_slice(&mut self, values: &[T]) {
        let new_end = self.end + values.len();
        assert!(new_end <= self.data.len(), "stack overflow");
        self.data[self.end..new_end].copy_from_slice(values);
        self.end = new_end;
    }

    #[inline(always)]
    pub(crate) fn last_mut(&mut self) -> Option<&mut T> {
        assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
        if unlikely(self.end == 0) {
            None
        } else {
            Some(&mut self.data[self.end - 1])
        }
    }

    #[inline(always)]
    pub(crate) fn last(&self) -> Option<&T> {
        assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
        if unlikely(self.end == 0) {
            None
        } else {
            Some(&self.data[self.end - 1])
        }
    }

    #[inline(always)]
    pub(crate) fn drain(&mut self, range: impl RangeBounds<usize>) -> Cow<'_, [T]> {
        let start = match range.start_bound() {
            core::ops::Bound::Included(&start) => start,
            core::ops::Bound::Excluded(&start) => start + 1,
            core::ops::Bound::Unbounded => 0,
        };
        let end = match range.end_bound() {
            core::ops::Bound::Included(&end) => end + 1,
            core::ops::Bound::Excluded(&end) => end,
            core::ops::Bound::Unbounded => self.end,
        };

        assert!(start <= end);
        assert!(end <= self.end);

        if end == self.end {
            self.end = start;
            return Cow::Borrowed(&self.data[start..end]);
        }

        let drain = self.data[start..end].to_vec();
        self.data.copy_within(end..self.end, start);
        self.end -= end - start;
        Cow::Owned(drain)
    }
}

impl<T> core::ops::Index<usize> for BoxVec<T> {
    type Output = T;

    #[inline(always)]
    fn index(&self, index: usize) -> &T {
        &self.data[index]
    }
}

impl<T> core::ops::Index<core::ops::Range<usize>> for BoxVec<T> {
    type Output = [T];

    #[inline(always)]
    fn index(&self, index: core::ops::Range<usize>) -> &[T] {
        &self.data[index]
    }
}

impl<T> core::ops::IndexMut<usize> for BoxVec<T> {
    #[inline(always)]
    fn index_mut(&mut self, index: usize) -> &mut T {
        &mut self.data[index]
    }
}