o3 0.32.0

shared-nothing primitives
Documentation
pub mod common;

use std::{cell::Cell, mem};

use common::{Counted, Order, alloc::system::Tracker, depot::Fault};
use o3::depot::array::{self, IntoIter, Len, init};

#[global_allocator]
static ALLOCATOR: Tracker = Tracker;

#[test]
fn guard_restores_prefix() {
    let counts = [const { Cell::new(Some(0)) }; 3];
    let mut values = counts.each_ref().map(|count| Some(Counted(count)));
    let mut backing = mem::MaybeUninit::uninit();
    let mut guard = init::Guard::<_, 3>::new(&mut backing);
    for _ in 0..2 {
        let vacant = guard.next().continue_value().expect("remaining position");
        let value = vacant.get_mut(&mut values).take().expect("owned value");
        guard = vacant.insert(value);
    }
    assert!(values[0].is_none() && values[1].is_none());
    for (value, slot) in guard.into_iter().zip(&mut values) {
        *slot = Some(value);
    }
    assert!(values.iter().all(Option::is_some));
    assert_eq!(counts.each_ref().map(Cell::get), [Some(0); 3]);
    drop(values);
    assert_eq!(counts.each_ref().map(Cell::get), [Some(1); 3]);
}

#[test]
fn guard_drains_prefix() {
    let order = Cell::new(Some(0));
    let mut backing = mem::MaybeUninit::uninit();
    let mut guard = init::Guard::<_, 4>::new(&mut backing);
    for value in 1..=3 {
        guard = guard
            .next()
            .continue_value()
            .expect("remaining position")
            .insert(Order(&order, value));
    }
    let mut values = guard.into_iter();
    let first = values.next().expect("initialized prefix");
    assert_eq!((first.1, values.len()), (1, 2));
    values.as_mut_slice()[0].1 = 4;
    drop(values);
    assert_eq!(order.get(), Some(43));
    drop(first);
    assert_eq!(order.get(), Some(431));

    order.set(Some(0));
    let mut guard = init::Guard::<_, 4, true>::new(&mut backing);
    for value in 1..=3 {
        guard = guard
            .next()
            .continue_value()
            .expect("remaining position")
            .insert(Order(&order, value));
    }
    let mut values = guard.into_iter();
    let first = values.next().expect("initialized prefix");
    assert_eq!((first.1, values.len()), (1, 2));
    drop(values);
    assert_eq!(order.get(), Some(32));
    drop(first);
    assert_eq!(order.get(), Some(321));
}

#[test]
fn prefix_reuses_backing() {
    use o3::alloc::BoxArrayExt as _;

    let orders = [const { Cell::new(Some(0)) }; 2];
    let mut backing = Box::<[mem::MaybeUninit<Order<'static>>; 3]>::try_uninit()
        .expect("reusable prefix backing");
    let pointer = backing.as_ptr().cast::<()>();
    for (index, order) in orders.iter().enumerate() {
        let (result, activity) = Tracker::measure(|| {
            init::Prefix::<Order<'_>, 3>::with(&mut backing, |prefix| {
                assert!(prefix.as_slice().is_empty());
                let first = prefix
                    .vacancy()
                    .expect("first position")
                    .insert(Order(order, 1));
                let second = prefix
                    .vacancy()
                    .expect("second position")
                    .insert(Order(order, 2));
                assert_eq!(prefix.get(first).1, 1);
                prefix.get_mut(second).1 = 3;
                assert_eq!(prefix.as_slice().as_ptr().cast::<()>(), pointer);
                if index == 0 {
                    return Err(Fault::Element(2));
                }
                prefix
                    .vacancy()
                    .expect("last position")
                    .insert(Order(order, 4));
                assert!(prefix.vacancy().is_none());
                Ok(prefix.as_slice().len())
            })
        });
        assert_eq!(activity, [0; 4]);
        assert_eq!(
            result,
            if index == 0 {
                Err(Fault::Element(2))
            } else {
                Ok(3)
            }
        );
        assert_eq!(order.get(), Some(if index == 0 { 13 } else { 134 }));
    }
    drop(backing);
    assert_eq!(orders.each_ref().map(Cell::get), [Some(13), Some(134)]);
}

#[test]
fn scoped_prefix_capacity() {
    let mut empty: [mem::MaybeUninit<u8>; 0] = [];
    init::Prefix::<u8, 0>::with(&mut empty, |prefix| {
        assert!(prefix.vacancy().is_none());
        assert!(prefix.as_slice().is_empty());
    });
    let mut backing = [mem::MaybeUninit::<u8>::uninit(); 256];
    init::Prefix::<u8, 256>::with(&mut backing, |prefix| {
        for value in u8::MIN..=u8::MAX {
            let key = prefix.vacancy().expect("bounded position").insert(value);
            assert_eq!(*key.as_inner(), value);
            assert_eq!(*prefix.get(key), value);
        }
        assert!(prefix.vacancy().is_none());
        assert_eq!(prefix.as_slice().len(), 256);
    });
}

#[test]
fn mapped_prefix_reuses() {
    use o3::alloc::BoxArrayExt as _;

    let mut input = [const { mem::MaybeUninit::<Cell<Option<usize>>>::uninit() }; 4];
    let mut backing =
        Box::<[mem::MaybeUninit<[usize; 2]>; 4]>::try_uninit().expect("reusable mapping backing");
    let pointer = backing.as_ptr().cast::<()>();
    let ((), activity) = Tracker::measure(|| {
        init::Prefix::<Cell<Option<usize>>, 4>::with(&mut input, |source| {
            for _ in 0..4 {
                source
                    .vacancy()
                    .expect("source position")
                    .insert(Cell::new(Some(0)));
            }
            let mut next = 0;
            let (guard, error) = init::Guard::<_, 4>::try_map(&mut backing, source, |value| {
                let index = next;
                next += 1;
                match index {
                    1 => Ok(None),
                    3 => Err(Fault::Element(3)),
                    _ => Ok(Some(Counted(value))),
                }
            })
            .err()
            .expect("partial mapping");
            assert_eq!(error, Fault::Element(3));
            assert_eq!(guard.as_slice().len(), 2);
            assert_eq!(guard.as_slice().as_ptr().cast::<()>(), pointer);
            assert!(std::ptr::eq(guard.as_slice()[1].0, &source.as_slice()[2]));
            assert!(source.as_slice().iter().map(Cell::get).eq([Some(0); 4]));
            let mut values = guard.into_iter();
            let first = values.next().expect("first mapped value");
            drop(values);
            assert!(source.as_slice().iter().map(Cell::get).eq([
                Some(0),
                Some(0),
                Some(1),
                Some(0)
            ]));
            drop(first);

            let guard = init::Guard::<_, 4>::try_map(&mut backing, source, |value| {
                Ok::<_, Fault>(Some(Counted(value)))
            })
            .ok()
            .expect("complete mapping");
            assert_eq!(guard.as_slice().as_ptr().cast::<()>(), pointer);
            drop(guard);
            assert!(source.as_slice().iter().map(Cell::get).eq([
                Some(2),
                Some(1),
                Some(2),
                Some(1)
            ]));
        });
    });
    assert_eq!(activity, [0; 4]);
}

#[test]
fn iter_range_and_drop() {
    assert_eq!(
        mem::size_of::<array::Vec<u64, 4>>(),
        5 * mem::size_of::<usize>()
    );
    assert_eq!(
        mem::size_of::<IntoIter<u64, 4>>(),
        mem::size_of::<array::Vec<u64, 4>>()
    );

    let counts = [const { Cell::new(Some(0)) }; 4];
    let mut values =
        array::Vec::<_, 4>::from_fn(Len::MAX, |slot| Counted(&counts[slot.index()])).into_iter();
    assert!(values.next().is_some());
    assert_eq!(values.count(), 3);
    assert_eq!(counts.each_ref().map(Cell::get), [Some(1); 4]);

    let mut values = array::Vec::<_, 4>::from_fn(Len::MAX, |slot| Counted(&counts[slot.index()]));
    {
        let mut drained = values.drain();
        drop(drained.next().expect("first value"));
    }
    assert_eq!(counts.each_ref().map(Cell::get), [Some(2); 4]);
    assert_eq!(values.extent().get(), 0);
    values
        .vacancy()
        .expect("drained position")
        .insert(Counted(&counts[0]));
    drop(values);
    assert_eq!(counts.each_ref().map(Cell::get), [3, 2, 2, 2].map(Some));
}