shared-vec 0.1.0

Efficient shared container types
Documentation
//! Basic tests for [`shared_vec`] crate

use shared_vec::{Counter, String, Vec};
use std::borrow::{Borrow, ToOwned};
use std::cell::Cell;
use std::collections::HashSet;
use std::hash::{DefaultHasher, Hash, Hasher, RandomState};
use std::ops::Bound;
use std::sync::atomic::AtomicUsize;

#[expect(clippy::reversed_empty_ranges)]
fn test_vec<C: Counter<usize>>() {
    assert_eq!(Vec::<C, u8>::new().len(), 0);
    assert_eq!(Vec::<C, u8>::default().len(), 0);

    let vec = [1, 2, 3, 4, 5];

    let v = Vec::<C, _>::from_boxed_slice(Box::new([1, 2, 3, 4, 5]));
    assert_eq!(v.len(), 5);
    assert_eq!(&*v, &[1, 2, 3, 4, 5]);
    assert_eq!(v, v.idx(0..5));
    assert_eq!(v, Vec::<C, _>::from(Box::new([1, 2, 3, 4, 5]) as Box<[_]>));

    assert_eq!(format!("{v:?}"), "[1, 2, 3, 4, 5]");

    let v2 = v.clone();
    assert_eq!(v2.len(), 5);
    assert_eq!(&*v2, &[1, 2, 3, 4, 5]);

    let v3 = v.get(1..4).unwrap();
    assert_eq!(v3.len(), 3);
    assert_eq!(&*v3, &[2, 3, 4]);

    let v4 = v.idx(0..2);
    assert_eq!(v4.len(), 2);
    assert_eq!(&*v4, &[1, 2]);

    for i in 0..v.len() {
        assert_eq!(v.idx(i..i + 1).len(), 1);
        assert_eq!(v.idx(i..i + 1).as_slice(), &[v.as_slice()[i]]);
    }

    let mut s = v.clone();
    for i in 0..v.len() {
        assert_eq!(s, v.idx(i..));
        s = s.idx(1..);
        assert_eq!(s.as_original_slice(), vec.as_slice());
    }

    let mut s = v.clone();
    for i in (0..v.len()).rev() {
        assert_eq!(s, v.idx(..(i + 1)));
        s = s.idx(..i);
        assert_eq!(s.as_original_slice(), vec.as_slice());
    }

    macro_rules! idx_ok {
        ($bounds:expr, $slice:expr) => {
            let bounds = $bounds;
            let slice: &[i32] = $slice;
            let v3 = v.get(bounds.clone()).unwrap();
            assert_eq!(v3.len(), slice.len());
            assert_eq!(&*v3, slice);
            assert_eq!(v3.as_slice(), &vec[bounds.clone()]);
            assert_eq!(<_ as AsRef<[_]>>::as_ref(&v3), &vec[bounds.clone()]);
            assert_eq!(<_ as Borrow<[_]>>::borrow(&v3), &vec[bounds.clone()]);

            let mut a = DefaultHasher::new();
            v3.hash(&mut a);
            let mut b = DefaultHasher::new();
            slice.hash(&mut b);
            assert_eq!(a.finish(), b.finish());

            assert!(v.is_valid_range(bounds.clone()));
            assert_eq!(v3, unsafe { v.get_unchecked(bounds) });
        };
    }

    idx_ok!(0..0, &[]);
    idx_ok!(2..2, &[]);
    idx_ok!(5..5, &[]);
    idx_ok!((Bound::Excluded(1), Bound::Included(1)), &[]);
    idx_ok!(..3, &[1, 2, 3]);
    idx_ok!(2.., &[3, 4, 5]);
    idx_ok!(1..4, &[2, 3, 4]);
    idx_ok!(1..=3, &[2, 3, 4]);

    macro_rules! idx_err {
        ($bounds:expr) => {
            assert!(v.get($bounds).is_none());
            assert!(vec.get($bounds).is_none());
        };
    }

    idx_err!(4..2);
    idx_err!(..6);
    idx_err!(6..);
    idx_err!((usize::MAX)..);
    idx_err!(..(usize::MAX));
    idx_err!(..=(usize::MAX));
    idx_err!((Bound::Excluded(usize::MAX), Bound::Unbounded));

    let integers: [&[i32]; 3] = [&[0, 1], &[3], &[6, 7, 8]];
    assert!(integers.is_sorted());

    let integers = integers
        .iter()
        .copied()
        .map(|s| Vec::<C, i32>::from_boxed_slice(s.into()))
        .collect::<std::vec::Vec<Vec<C, i32>>>();
    for s in integers.iter() {
        assert!(integers.binary_search(s).is_ok());
    }
    assert!(integers.is_sorted());

    let integers = Vec::<C, i32>::from_boxed_slice(Box::new([5, 3, 1, 4, 2]));
    let slices = [
        integers.idx(2..=2),
        integers.idx(2..),
        integers.idx(4..),
        integers.idx(1..),
        integers.idx(0..2),
        integers.idx(0..),
    ];
    assert!(slices.is_sorted());
}

#[test]
fn rc_vec() {
    test_vec::<Cell<usize>>();
}

#[test]
fn arc_vec() {
    test_vec::<AtomicUsize>();
}

#[expect(clippy::reversed_empty_ranges)]
fn test_string<C: Counter<usize>>() {
    assert_eq!(String::<C>::new().len(), 0);
    assert_eq!(String::<C>::new().as_str(), "");
    assert_eq!(String::<C>::new(), String::<C>::default());
    assert_eq!(String::<C>::new(), String::<C>::from_boxed_str("".into()));
    assert_eq!(
        String::<C>::new(),
        String::<C>::from_utf8(b"".to_vec().into_boxed_slice()).unwrap()
    );

    let string = "hello 🦀!".to_owned();

    assert_eq!(
        String::<C>::from_boxed_str("hello 🦀!".into()),
        String::<C>::from_utf8("hello 🦀!".as_bytes().to_vec().into_boxed_slice()).unwrap()
    );
    assert_eq!(String::<C>::from_boxed_str("hello 🦀!".into()), unsafe {
        String::<C>::from_utf8_unchecked("hello 🦀!".as_bytes().to_vec().into_boxed_slice())
    });
    assert_eq!(String::<C>::from_boxed_str("hello 🦀!".into()), unsafe {
        String::<C>::from_utf8_unchecked("hello 🦀!".as_bytes().to_vec().into())
    });
    assert_eq!(
        String::<C>::from_boxed_str("hello 🦀!".into()),
        String::<C>::from("hello 🦀!".to_owned().into_boxed_str())
    );

    assert!(String::<C>::from_utf8(b"\xFF".to_vec().into_boxed_slice()).is_err());

    let s = String::<C>::from_boxed_str("hello 🦀!".to_owned().into_boxed_str());
    assert_eq!(s.len(), 11);
    assert_eq!(s.as_str(), "hello 🦀!");

    let s2 = s.clone();
    assert_eq!(s2.len(), 11);
    assert_eq!(s2.as_str(), "hello 🦀!");

    macro_rules! idx_ok {
        ($bounds:expr, $string:literal) => {
            let bounds = $bounds;
            let s3 = s.idx(bounds.clone());
            assert_eq!(s3.as_str(), &string[bounds.clone()]);
            assert_eq!(<_ as AsRef<str>>::as_ref(&s3), &string[bounds.clone()]);
            assert_eq!(<_ as Borrow<str>>::borrow(&s3), &string[bounds.clone()]);
            assert_eq!(<_ as AsRef<[u8]>>::as_ref(&s3), string[bounds].as_bytes());
            assert_eq!(s3.len(), $string.len());
            assert_eq!(s3.is_empty(), $string.is_empty());
            assert_eq!(s3.as_str(), $string);
            assert_eq!(s3.as_original_str(), string);
            assert_eq!(s3.to_string(), $string);
            assert_eq!(s3.bytes(), $string.as_bytes());
            assert_eq!(format!("{s3:?}"), format!("{:?}", $string));

            assert!(s.is_valid_range($bounds));

            let mut a = DefaultHasher::new();
            s3.hash(&mut a);
            let mut b = DefaultHasher::new();
            $string.hash(&mut b);
            assert_eq!(a.finish(), b.finish());

            let s4 = unsafe { s.get_unchecked($bounds) };
            assert_eq!(s4.len(), $string.len());
            assert_eq!(s4.is_empty(), $string.is_empty());
            assert_eq!(s4.as_str(), $string);
            assert_eq!(s4.as_original_str(), string);
            assert_eq!(s4.to_string(), $string);
            assert_eq!(s4.bytes(), $string.as_bytes());
            assert_eq!(format!("{s4:?}"), format!("{:?}", $string));

            assert_eq!(s3, s4);
        };
    }

    idx_ok!(0..0, "");
    idx_ok!(1..1, "");
    idx_ok!(11..11, "");
    idx_ok!((Bound::Excluded(1), Bound::Excluded(2)), "");
    idx_ok!(..5, "hello");
    idx_ok!(..11, "hello 🦀!");
    idx_ok!(..=10, "hello 🦀!");
    idx_ok!(..=9, "hello 🦀");
    idx_ok!(.., "hello 🦀!");
    idx_ok!(6..10, "🦀");
    idx_ok!(6.., "🦀!");
    idx_ok!(6..=10, "🦀!");

    macro_rules! idx_err {
        ($bounds:expr) => {
            let s3 = s.get($bounds);
            assert!(s3.is_none());
        };
    }

    idx_err!(4..2);
    idx_err!(6..9);
    idx_err!(7..10);
    idx_err!(7..);
    idx_err!(..9);
    idx_err!(..12);
    idx_err!(12..);
    idx_err!((usize::MAX)..);
    idx_err!(..(usize::MAX));
    idx_err!(..=(usize::MAX));
    idx_err!((Bound::Excluded(usize::MAX), Bound::Unbounded));

    let strings = [
        "apple",
        "banana",
        "cherry",
        "date",
        "elderberry",
        "fig",
        "grape",
    ];
    let strings = strings
        .iter()
        .map(|s| String::<C>::from_boxed_str((*s).to_owned().into_boxed_str()))
        .collect::<std::vec::Vec<String<C>>>();
    for s in strings.iter() {
        assert!(strings.binary_search(s).is_ok());
    }
    assert!(strings.is_sorted());
    let set: HashSet<String<C>, RandomState> = HashSet::from_iter(strings.iter().cloned());
    assert_eq!(set.len(), strings.len());
}

#[test]
fn rc_string() {
    test_string::<Cell<usize>>();
}

#[test]
fn arc_string() {
    test_string::<AtomicUsize>();
}