sharky-arrayvec 0.3.0

An array backed vector
Documentation
use core::marker::Destruct;
use core::mem::MaybeUninit;
use core::ops;

use super::ArrayVec;

pub(crate) const fn write_filled_const<T: [const] Clone + [const] Destruct>(
    slice: &mut [MaybeUninit<T>],
    value: T,
) {
    if slice.is_empty() {
        return;
    }

    // NOTE: `.wrapping_sub` can be used here because slice.len() is greater than
    // zero as checked above.
    konst::iter::for_each! { elem in konst::slice::get_up_to_mut(slice, slice.len().wrapping_sub(1)).unwrap() =>
        elem.write(value.clone());
    }
    slice[slice.len().wrapping_sub(1)].write(value);
}

impl<const C: usize, T> const ops::Deref for ArrayVec<C, T> {
    type Target = [T];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_slice()
    }
}

impl<const C: usize, T> const ops::DerefMut for ArrayVec<C, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_slice_mut()
    }
}

impl<const C: usize, T> const ops::Index<usize> for ArrayVec<C, T> {
    type Output = T;

    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        &self.as_slice()[index]
    }
}

impl<const C: usize, T> const ops::IndexMut<usize> for ArrayVec<C, T> {
    #[inline]
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.as_slice_mut()[index]
    }
}
impl<const C: usize, T> const Default for ArrayVec<C, T> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<const C: usize, T: PartialEq> PartialEq for ArrayVec<C, T> {
    #[inline]
    default fn eq(&self, other: &ArrayVec<C, T>) -> bool {
        self.len() == other.len() && self.as_slice().eq(other.as_slice())
    }
}

impl<const C: usize, T: [const] PartialEq> const PartialEq for ArrayVec<C, T> {
    #[inline]
    fn eq(&self, other: &ArrayVec<C, T>) -> bool {
        self.len() == other.len() && self.as_slice().eq(other.as_slice())
    }
}

impl<const C: usize, T: [const] Eq> const Eq for ArrayVec<C, T> {}

impl<const C: usize, T: PartialOrd> PartialOrd for ArrayVec<C, T> {
    #[inline]
    default fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.as_slice().partial_cmp(other.as_slice())
    }
}

impl<const C: usize, T: [const] PartialOrd> const PartialOrd for ArrayVec<C, T> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.as_slice().partial_cmp(other.as_slice())
    }
}

impl<const C: usize, T: Ord> Ord for ArrayVec<C, T> {
    #[inline]
    default fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.as_slice().cmp(other.as_slice())
    }
}

impl<const C: usize, T: [const] Ord> const Ord for ArrayVec<C, T> {
    #[inline]
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.as_slice().cmp(other.as_slice())
    }
}

impl<const C: usize, T> ArrayVec<C, T> {
    /// See [`ArrayVec::from_slice`].
    #[inline]
    pub const fn from_slice_const(slice: &[T]) -> Option<Self>
    where
        T: const Clone, {
        if slice.len() > C {
            return None;
        }

        let mut array = konst::maybe_uninit::UNINIT_ARRAY::<T, C>::V;

        konst::iter::for_each! {idx in 0..slice.len() =>
            let item = slice[idx].clone();
            array[idx] = MaybeUninit::new(item);
        }

        Some(Self {
            array,
            len: slice.len(),
        })
    }

    /// See [`ArrayVec::truncate`].
    #[inline]
    pub const fn truncate_const(&mut self, len: usize)
    where
        T: [const] Destruct, {
        if len >= self.len {
            return;
        }

        // SAFETY: These elements can be dropped because everything from `0..self.len`
        // is initialized. The dropped elements will not be accessed again because
        // `self.len` is set to `len`
        unsafe { self.array[len..self.len].assume_init_drop() };
        self.len = len;
    }

    /// See [`ArrayVec::resize`].
    ///
    /// # Panics
    ///
    /// This function panics when:
    ///
    /// 1. `new_len > C`
    #[inline]
    pub const fn resize_const(&mut self, new_len: usize, value: T)
    where
        T: [const] Clone + [const] Destruct, {
        assert!(new_len <= C, "Tried to resize beyond capacity.");

        if new_len < self.len {
            self.truncate_const(new_len);
        } else if new_len > self.len {
            write_filled_const(&mut self.array[self.len..new_len], value);
            self.len = new_len;
        }
    }
}

#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {

    use super::*;
    use proptest::prelude::*;

    fn proptest_config() -> ProptestConfig {
        ProptestConfig {
            #[cfg(miri)]
            failure_persistence: None,
            #[cfg(miri)]
            cases: 32,
            ..ProptestConfig::default()
        }
    }

    #[test]
    fn const_partial_eq() {
        static A: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
        static B: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
        static C: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 4]);

        assert_eq!(A, B);
        assert_ne!(A, C);
    }

    #[test]
    fn const_ord() {
        static A: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 3]);
        static B: ArrayVec<3, u8> = ArrayVec::from_array([1u8, 2, 4]);
        assert!(A < B);
    }

    #[test]
    fn write_filled_const_empty() {
        let mut vec: ArrayVec<0, ()> = ArrayVec::default();
        write_filled_const(&mut vec.array, ());
        assert!(vec.is_empty());
    }

    #[test]
    fn from_slice_const() {
        const EXPECTED: [u32; 4] = [67, 69, 420, 80085];
        let vec: ArrayVec<4, u32> = ArrayVec::<4, _>::from_slice_const(&EXPECTED).unwrap();
        assert_eq!(vec.as_slice(), EXPECTED);
    }

    #[test]
    fn from_slice_overflow() {
        assert!(ArrayVec::<4, u32>::from_slice_const(&[1; 8]).is_none());
    }

    #[test]
    fn truncate_const() {
        let mut vec = ArrayVec::from_array([1, 2, 3, 4]);

        vec.truncate_const(2);
        assert_eq!(vec.as_slice(), [1, 2]);
        vec.push(0);
        assert_eq!(vec.as_slice(), [1, 2, 0]);
        vec.truncate_const(0);
        assert_eq!(vec.as_slice(), []);
        vec.truncate_const(0xcafebabe);
        assert!(vec.is_empty());
    }

    #[test]
    fn truncate_const_heap_types() {
        let mut vec = ArrayVec::from_array([String::from("hey"), String::from("boy")]);

        vec.truncate_const(1);
        assert_eq!(vec.as_slice(), [String::from("hey")]);
        core::mem::drop(vec);
    }

    #[test]
    fn truncate_const_noop() {
        let mut vec = ArrayVec::from_array([1, 2, 3, 4]);

        // Anything >= len is a no-op
        vec.truncate_const(4);
        assert_eq!(vec.len(), 4);
        vec.truncate_const(10);
        assert_eq!(vec.len(), 4);
    }

    #[derive(Debug, Clone)]
    enum Op {
        Push(u8),
        TryPush(u8),
        Pop,
        Insert(u8, usize),
        TryInsert(u8, usize),
        Truncate(usize),
        Resize(usize, u8),
        TruncateConst(usize),
        ResizeConst(usize, u8),
    }

    fn arbatrary_op() -> impl Strategy<Value = Op> {
        prop_oneof![
            any::<u8>().prop_map(Op::Push),
            any::<u8>().prop_map(Op::TryPush),
            Just(Op::Pop),
            (any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::Insert(v, i)),
            (any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::TryInsert(v, i)),
            any::<usize>().prop_map(Op::Truncate),
            (any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::Resize(i, v)),
            any::<usize>().prop_map(Op::TruncateConst),
            (any::<u8>(), any::<usize>()).prop_map(|(v, i)| Op::ResizeConst(i, v)),
        ]
    }

    fn model_try_push(v: &mut Vec<u8>, value: u8) -> Result<(), u8> {
        if v.len() >= v.capacity() {
            return Err(value);
        }

        v.push(value);
        Ok(())
    }

    fn model_try_insert(v: &mut Vec<u8>, value: u8, idx: usize) -> Result<(), u8> {
        if v.len() >= v.capacity() || idx > v.len() {
            return Err(value);
        }

        v.insert(idx, value);
        Ok(())
    }

    proptest! {
        #![proptest_config(proptest_config())]
        #[test]
        fn model_based(ops in prop::collection::vec(arbatrary_op(), 0..50)) {
            const C: usize = 8;
            let mut vec: ArrayVec<C, u8> = ArrayVec::new();
            let mut model: Vec<u8> = Vec::with_capacity(C);

            for op in ops {
                match op {
                    Op::Push(v) => {
                        if model.len() < C {
                            vec.push(v);
                            model.push(v);
                        }
                    }
                    Op::TryPush(x) => {
                        assert_eq!(vec.try_push(x), model_try_push(&mut model, x));
                    }
                    Op::Pop => {
                        assert_eq!(vec.pop(), model.pop());
                    }
                    Op::Insert(v, i) => {
                        if model.len() < C {
                            let idx = i % (model.len() + 1);
                            vec.insert(v, idx);
                            model.insert(idx, v);
                        }
                    }
                    Op::TryInsert(val, idx) => {
                        assert_eq!(
                            vec.try_insert(val, idx),
                            model_try_insert(&mut model, val, idx)
                        );
                    }
                    Op::Truncate(n) => {
                        let n = n % (C + 1);
                        vec.truncate(n);
                        model.truncate(n);
                    }
                    Op::Resize(n, x) => {
                        let n = n % (C + 1);
                        vec.resize(n, x);
                        model.resize(n,x);
                    }
                    Op::TruncateConst(n) => {
                        let n = n % (C + 1);
                        vec.truncate_const(n);
                        model.truncate(n);
                    }
                    Op::ResizeConst(n, x) => {
                        let n = n % (C + 1);
                        vec.resize_const(n, x);
                        model.resize(n,x);
                    }
                }
                assert_eq!(vec.as_slice(), model.as_slice());
                assert!(vec.len() <= C);
            }
        }
    }
}