o3 0.7.0

shared-nothing primitives
Documentation
use o3::collections::BatchSet;

#[test]
fn coalesces_across_the_draining_and_pending_batches() {
    let set = BatchSet::with_capacity(4);
    assert!(set.insert(0));
    assert!(set.insert(1));

    let mut batch = set.drain_batch().unwrap();
    assert_eq!(batch.next(), Some(0));
    assert!(set.insert(0));
    assert!(!set.insert(1));
    assert_eq!(batch.next(), Some(1));
    assert_eq!(batch.next(), None);
    drop(batch);

    assert_eq!(set.drain_batch().unwrap().collect::<Vec<_>>(), [0]);
    assert!(set.is_empty());
}

#[test]
fn dropping_a_partial_batch_returns_each_index_once() {
    let set = BatchSet::with_capacity(8);
    for index in 0..4 {
        assert!(set.insert(index));
    }

    let mut batch = set.drain_batch().unwrap();
    assert_eq!(batch.next(), Some(0));
    assert!(set.insert(0));
    assert!(!set.insert(2));
    drop(batch);

    let mut returned = set.drain_batch().unwrap().collect::<Vec<_>>();
    returned.sort_unstable();
    assert_eq!(returned, [0, 1, 2, 3]);
    assert!(set.is_empty());
}

#[test]
fn rejects_nested_drains_without_disturbing_the_live_batch() {
    let set = BatchSet::with_capacity(2);
    assert!(set.insert(1));

    let mut batch = set.drain_batch().unwrap();
    assert!(set.drain_batch().is_none());
    assert_eq!(batch.next(), Some(1));
    assert_eq!(batch.next(), None);
    drop(batch);

    assert!(set.is_empty());
}

#[test]
fn grows_both_generations_across_summary_levels() {
    let set = BatchSet::with_capacity(1);
    assert!(set.insert(0));
    set.grow_to(1 << 20);
    assert!(set.insert(900_001));

    let mut batch = set.drain_batch().unwrap();
    assert_eq!(batch.next(), Some(0));
    assert!(set.insert(0));
    assert_eq!(batch.next(), Some(900_001));
    drop(batch);

    assert_eq!(set.pop(), Some(0));
    assert!(set.is_empty());
}

#[test]
fn remove_unlinks_an_index_from_either_batch() {
    let set = BatchSet::with_capacity(3);
    assert!(set.insert(0));
    assert!(set.insert(1));

    let mut batch = set.drain_batch().unwrap();
    assert_eq!(batch.next(), Some(0));
    assert!(set.insert(0));
    assert!(set.remove(0));
    assert!(set.remove(1));
    assert!(!set.remove(2));
    assert_eq!(batch.next(), None);
    drop(batch);

    assert!(set.is_empty());
}

#[test]
fn churn_matches_a_reference_set_across_partial_batches() {
    const CAPACITY: usize = 4_097;
    let set = BatchSet::with_capacity(CAPACITY);
    let mut pending = std::collections::BTreeSet::new();
    let mut state = 0x9e37_79b9_7f4a_7c15u64;

    let mut random_index = || {
        state ^= state << 7;
        state ^= state >> 9;
        state ^= state << 8;
        state as usize % CAPACITY
    };

    for _ in 0..128 {
        for _ in 0..64 {
            let index = random_index();
            assert_eq!(set.insert(index), pending.insert(index));
        }

        let mut current = std::mem::take(&mut pending);
        let mut batch = set.drain_batch().unwrap();
        assert!(set.drain_batch().is_none());

        for step in 0..96 {
            let index = random_index();
            match step % 3 {
                0 => {
                    if let Some(index) = batch.next() {
                        assert!(current.remove(&index));
                        if index & 1 == 0 {
                            assert!(set.insert(index));
                            assert!(pending.insert(index));
                        }
                    }
                }
                1 => {
                    let expected = !current.contains(&index) && pending.insert(index);
                    assert_eq!(set.insert(index), expected);
                }
                _ => {
                    let expected = current.remove(&index) || pending.remove(&index);
                    assert_eq!(set.remove(index), expected);
                }
            }
            assert_eq!(set.len(), current.len() + pending.len());
        }

        drop(batch);
        pending.append(&mut current);
        assert_eq!(set.len(), pending.len());
    }

    let mut actual = set.drain_batch().unwrap().collect::<Vec<_>>();
    actual.sort_unstable();
    assert_eq!(actual, pending.into_iter().collect::<Vec<_>>());
    assert!(set.is_empty());
}