heterovec 0.1.2

In-place vector of dynamically sized type

use crate::HeteroSizedVec;

#[test]
fn closure_basic() {
    #[inline(never)]
    fn closure(n: usize) -> impl Fn() -> usize {
        move || n
    }

    let mut vec: HeteroSizedVec<dyn Fn() -> usize> = HeteroSizedVec::new();
    for n in 0..10 {
        vec.push_value(closure(n));
    }
    for (i, func) in vec.iter().enumerate() {
        assert_eq!(i, func());
    }
}

#[test]
fn array_basic() {
    let mut vec: HeteroSizedVec<[usize]> = HeteroSizedVec::new();

    vec.push_value([0]);
    vec.push_value([1, 2]);
    vec.push_value([3, 4, 5]);
    vec.push_value([6, 7, 8, 9]);
    vec.push_value([10, 11, 12, 13, 14]);

    assert_eq!(&vec[0], &[0]);
    assert_eq!(&vec[1], &[1, 2]);
    assert_eq!(&vec[2], &[3, 4, 5]);
    assert_eq!(&vec[3], &[6, 7, 8, 9]);
    assert_eq!(&vec[4], &[10, 11, 12, 13, 14]);
}

#[test]
fn array_copy_slice_basic() {
    let mut vec: HeteroSizedVec<[usize]> = HeteroSizedVec::new();

    vec.push(&[0_usize] as &[_]);
    vec.push(&[1_usize, 2] as &[_]);
    vec.push(&[3_usize, 4, 5] as &[_]);
    vec.push(&[6_usize, 7, 8, 9] as &[_]);
    vec.push(&[10_usize, 11, 12, 13, 14] as &[_]);

    assert_eq!(&vec[0], &[0]);
    assert_eq!(&vec[1], &[1, 2]);
    assert_eq!(&vec[2], &[3, 4, 5]);
    assert_eq!(&vec[3], &[6, 7, 8, 9]);
    assert_eq!(&vec[4], &[10, 11, 12, 13, 14]);
}

#[test]
fn array_from_vec_basic() {
    let mut vec: HeteroSizedVec<[usize]> = HeteroSizedVec::new();

    vec.push(vec![0_usize]);
    vec.push(vec![1_usize, 2]);
    vec.push(vec![3_usize, 4, 5]);
    vec.push(vec![6_usize, 7, 8, 9]);
    vec.push(vec![10_usize, 11, 12, 13, 14]);

    assert_eq!(&vec[0], &[0]);
    assert_eq!(&vec[1], &[1, 2]);
    assert_eq!(&vec[2], &[3, 4, 5]);
    assert_eq!(&vec[3], &[6, 7, 8, 9]);
    assert_eq!(&vec[4], &[10, 11, 12, 13, 14]);
}

#[test]
fn str_basic() {
    let mut vec: HeteroSizedVec<str> = HeteroSizedVec::new();

    let strings = &["hello", "world", "foo", "bar", "baz"];

    for s in strings.iter().copied() {
        vec.push(s);
    }

    for (i, s) in strings.iter().copied().enumerate() {
        assert_eq!(&vec[i], s);
    }
}

#[test]
fn mutate_arrays() {
    let mut vec: HeteroSizedVec<[u8]> = HeteroSizedVec::new();

    vec.push_value([0]);
    vec.push_value([1, 2]);
    vec.push_value([3, 4, 5]);
    vec.push_value([6, 7, 8, 9]);

    let mut i = 0;
    for array in &mut vec {
        for elem in array {
            *elem = i;
            i += 1;
        }
    }

    for array in &mut vec {
        for elem in array {
            *elem *= 2;
        }
    }

    let mut i = 0;
    for array in &vec {
        for &elem in array {
            assert_eq!(elem, i * 2);
            i += 1;
        }
    }
}

#[test]
fn mutate_arrays_copy_slice() {
    let mut vec: HeteroSizedVec<[u8]> = HeteroSizedVec::new();

    vec.push(&[0_u8] as &[_]);
    vec.push(&[1_u8, 2] as &[_]);
    vec.push(&[3_u8, 4, 5] as &[_]);
    vec.push(&[6_u8, 7, 8, 9] as &[_]);

    let mut i = 0;
    for array in &mut vec {
        for elem in array {
            *elem = i;
            i += 1;
        }
    }

    for array in &mut vec {
        for elem in array {
            *elem *= 2;
        }
    }

    let mut i = 0;
    for array in &vec {
        for &elem in array {
            assert_eq!(elem, i * 2);
            i += 1;
        }
    }
}

#[test]
fn mutate_arrays_from_vec() {
    let mut vec: HeteroSizedVec<[u8]> = HeteroSizedVec::new();

    vec.push(vec![0]);
    vec.push(vec![1, 2]);
    vec.push(vec![3, 4, 5]);
    vec.push(vec![6, 7, 8, 9]);

    let mut i = 0;
    for array in &mut vec {
        for elem in array {
            *elem = i;
            i += 1;
        }
    }

    for array in &mut vec {
        for elem in array {
            *elem *= 2;
        }
    }

    let mut i = 0;
    for array in &vec {
        for &elem in array {
            assert_eq!(elem, i * 2);
            i += 1;
        }
    }
}

#[test]
fn closure_boxing() {
    #[inline(never)]
    fn closure(n: usize) -> impl Fn() -> usize {
        move || n
    }

    let mut vec: HeteroSizedVec<dyn Fn() -> usize> = HeteroSizedVec::new();
    for n in 0..10 {
        vec.push_value(closure(n));
    }

    let vec2: Vec<Box<dyn Fn() -> usize>> = vec.into_box_vec();

    for (i, func) in vec2.iter().enumerate() {
        assert_eq!(i, func());
    }
}

#[test]
#[should_panic]
fn index_out_of_bounds() {
    let mut vec: HeteroSizedVec<str> = HeteroSizedVec::new();

    for _ in 0..3 {
        vec.push("hello world");
    }

    for i in 0..4 {
        let _ = &vec[i];
    }
}

pub mod drop_test {
    #[test]
    #[should_panic]
    fn dangling_pointer_should_panic() {
        use std::mem::forget;

        let counter = DropTestCounter::new();
        let token = counter.token();
        forget(token);
        counter.check();
    }

    #[test]
    #[should_panic]
    fn double_free_should_panic() {
        use std::ptr::drop_in_place;
        use std::mem::drop;

        let counter = DropTestCounter::new();
        let mut token = counter.token();

        unsafe {
            drop_in_place(&mut token);
            drop(token);
        }

        counter.check();
    }

    #[test]
    fn drop_test_sanity_check() {
        let counter = DropTestCounter::new();

        let mut tokens = Vec::new();
        for _ in 0..100 {
            tokens.push(counter.token());
        }
        drop(tokens);

        counter.check();
    }

    use std::sync::{
        atomic::{Ordering, AtomicI64},
        Arc,
    };

    pub use std::mem::drop;

    #[derive(Clone)]
    pub struct DropTestCounter { alive_count: Arc<AtomicI64> }

    pub struct DropTestToken {
        alive_count: Arc<AtomicI64>,
        already_dropped: bool,
    }

    impl DropTestCounter {
        pub fn new() -> Self {
            DropTestCounter {
                alive_count: Arc::new(AtomicI64::new(0))
            }
        }

        pub fn token(&self) -> DropTestToken {
            self.alive_count.fetch_add(1, Ordering::Relaxed);
            DropTestToken {
                alive_count: self.alive_count.clone(),
                already_dropped: false,
            }
        }

        pub fn check(&self) {
            assert_eq!(
                self.alive_count.load(Ordering::Relaxed),
                0,
                "dangling pointer detected",
            );
        }
    }

    impl Drop for DropTestToken {
        fn drop(&mut self) {
            if self.already_dropped {
                panic!("double free detected");
            }

            self.already_dropped = true;
            self.alive_count.fetch_sub(1, Ordering::Relaxed);
        }
    }
}

#[test]
fn closure_drop_test() {
    use drop_test::*;

    #[inline(never)]
    fn closure(n: usize, token: DropTestToken) -> impl Fn() -> usize {
        move || {
            let token = &token;
            n
        }
    }

    let counter = DropTestCounter::new();

    let mut vec: HeteroSizedVec<dyn Fn() -> usize> = HeteroSizedVec::new();
    for n in 0..10 {
        vec.push_value(closure(n, counter.token()));
    }

    for (i, func) in vec.iter().enumerate() {
        assert_eq!(i, func());
    }

    drop(vec);
    counter.check();
}

#[test]
fn array_drop_test() {
    use drop_test::*;

    let counter = DropTestCounter::new();

    let mut vec: HeteroSizedVec<[DropTestToken]> = HeteroSizedVec::new();

    for i in 0..100 {
        let mut elem_vec: Vec<DropTestToken> = Vec::new();
        for _ in 0..i {
            elem_vec.push(counter.token());
        }
        vec.push(elem_vec);
    }

    drop(vec);

    counter.check();
}

#[test]
fn test_alignment() {
    pub trait Align {
        fn addr(&self) -> usize;

        fn align(&self) -> usize;
    }

    macro_rules! align_impl {
        ($align:expr)=>{{
            #[repr(align($align))]
            struct SpecialAlign(u8);

            impl Align for SpecialAlign {
                fn addr(&self) -> usize {
                    self as *const Self as usize
                }

                fn align(&self) -> usize { $align }
            }

            SpecialAlign(0)
        }};
    }

    let mut vec: HeteroSizedVec<dyn Align> = HeteroSizedVec::new();

    macro_rules! align_push_each {
        ($vec:expr, [$($align:expr),* $(,)?])=>{
            $(
            $vec.push_value(align_impl!($align));
            )*
        };
    }

    align_push_each!(vec, [2, 16, 32, 2, 2, 1024, 16, 16, 256, 32, 4]);

    for elem in &vec {
        assert!(elem.addr() % elem.align() == 0);
    }

}