idr-ebr 0.3.2

An efficient concurrent ID to object resolver
Documentation
use idr_ebr::{Config, EbrGuard, Idr, Key};

#[test]
fn smoke() {
    let idr: Idr<_> = Idr::default();

    assert_eq!(idr.iter(&EbrGuard::new()).count(), 0);

    // Insert values.
    let mut keys = (0..100)
        .map(|i| (idr.insert(i).unwrap(), i))
        .collect::<Vec<_>>();

    // Check that the values are accessible.
    for (key, value) in &keys {
        assert_eq!(&*idr.get(*key, &EbrGuard::new()).unwrap(), value);
    }

    check_iter(&idr, keys.clone());

    // Remove every other key.
    for (key, value) in &keys {
        if value % 2 == 0 {
            assert!(idr.remove(*key));
        }
    }

    // Check that remaining values are accessible.
    keys.retain(|(key, value)| {
        let guard = EbrGuard::new();
        let actual = idr.get(*key, &guard);

        if value % 2 == 0 {
            assert!(actual.is_none());
            false
        } else {
            assert_eq!(actual.unwrap(), *value);
            true
        }
    });

    check_iter(&idr, keys);

    fn check_iter(idr: &Idr<i32>, mut expected: Vec<(Key, i32)>) {
        let mut actual = idr
            .iter(&EbrGuard::new())
            .map(|(key, entry)| (key, *entry))
            .collect::<Vec<_>>();

        actual.sort();
        expected.sort();

        assert_eq!(actual, expected);
    }
}

#[test]
fn extension() {
    struct TinyConfig;
    impl Config for TinyConfig {
        const INITIAL_PAGE_SIZE: u32 = 4;
        const MAX_PAGES: u32 = 5;
        const RESERVED_BITS: u32 = 32;
    }

    let idr: Idr<_, TinyConfig> = Idr::new();
    assert_pages(&idr, 0);

    for expected_pages in 1..=TinyConfig::MAX_PAGES {
        let capacity = ((1 << (expected_pages - 1)) - 1) * TinyConfig::INITIAL_PAGE_SIZE;

        for i in 0..(2 << expected_pages) {
            idr.insert(capacity + i).unwrap();
        }
        assert_pages(&idr, expected_pages);

        let len = idr
            .iter(&EbrGuard::new())
            .enumerate()
            .inspect(|(i, (_, value))| assert_eq!(*value, u32::try_from(*i).unwrap()))
            .count();

        assert_eq!(
            u32::try_from(len).unwrap(),
            capacity + (2 << expected_pages)
        );
    }

    assert!(idr.insert(42).is_none());

    fn assert_pages(idr: impl std::fmt::Debug, expected: u32) {
        assert!(format!("{idr:?}").contains(&format!("allocated_pages: {expected}")));
    }
}

#[test]
fn reuse() {
    struct TinyConfig;
    impl Config for TinyConfig {
        const INITIAL_PAGE_SIZE: u32 = 4;
        const MAX_PAGES: u32 = 29;
        const RESERVED_BITS: u32 = 32;
    }

    assert!(format!("{:?}", TinyConfig::debug()).contains("GENERATION_BITS: 1"));

    let idr: Idr<_, TinyConfig> = Idr::new();

    let key = idr.insert(0).unwrap();
    assert!(idr.remove(key));

    let key2 = idr.insert(1).unwrap();
    assert!(!idr.contains(key));
    assert!(idr.remove(key2));

    let key3 = idr.insert(2).unwrap();
    assert_eq!(key3, key);
    assert_eq!(idr.get(key, &EbrGuard::new()).unwrap(), 2);
    assert!(idr.remove(key));
    assert!(!idr.contains(key3));
}

#[test]
fn invalid_key() {
    let idr = Idr::<i32>::default();
    let invalid_key = Key::try_from(1).unwrap();

    // Shouldn't panic.
    idr.get(invalid_key, &EbrGuard::new());
}

#[test]
fn to_owned() {
    let idr = Idr::<i32>::default();
    let key = idr.insert(42).unwrap();

    let guard = EbrGuard::new();
    let entry = idr.get(key, &guard).unwrap();
    let owned = entry.to_owned();
    assert_eq!(owned.clone().unwrap(), 42);

    assert!(idr.remove(key));
    assert!(entry.to_owned().is_some()); // `owned` is alive

    #[allow(clippy::clone_on_copy)]
    {
        assert_eq!(owned.unwrap(), *entry.clone());
    }

    assert!(entry.to_owned().is_none()); // `owned` is gone
}

#[test]
fn unused_vacant_entry() {
    let idr = Idr::<i32>::default();

    let key1 = idr.vacant_entry().unwrap().key();
    let key2 = idr.vacant_entry().unwrap().key();

    assert_eq!(key1, key2);
}

#[test]
fn debug() {
    let idr = Idr::<i32>::default();

    let guard = EbrGuard::new();
    assert_eq!(format!("{guard:?}"), "EbrGuard");

    let iter = idr.iter(&guard);
    assert_eq!(format!("{iter:?}"), "Iter { .. }");

    let key = idr.insert(42).unwrap();
    assert_eq!(format!("{:?}", idr.get_owned(key)), "Some(42)");
    assert_eq!(format!("{:?}", idr.get(key, &guard)), "Some(42)");

    let vacant = idr.vacant_entry().unwrap();
    assert!(format!("{vacant:?}").starts_with("VacantEntry { key: "));
}