o3 0.36.2

shared-nothing primitives
Documentation
pub mod common;

use std::num::NonZero;

use common::{
    alloc::system::{Activity, Tracker},
    buffer::pool::try_fill,
};
use o3::buffer::pool::{self, owners, state};

#[global_allocator]
static ALLOCATOR: Tracker = Tracker;

#[test]
fn admission_is_atomic() {
    let mut lease = (pool::Span::try_new(8)).expect("allocate the bounded destination");
    let source = b"abc";
    let (admitted, activity) = Tracker::activity(|| lease.try_append(source));
    assert!(activity == Activity::default());
    let append = (admitted).expect("admit the complete source");
    let (lease, activity) = Tracker::activity(|| append.commit());
    assert!(activity == Activity::default());
    assert_eq!(lease.as_slice(), source);
    let address = lease.as_slice().as_ptr();
    let result = lease.try_append(b"123456");
    let error = result
        .err()
        .expect("reject six bytes with only five remaining");
    assert_eq!(
        error.into_parts(),
        (NonZero::<usize>::MIN.saturating_add(5), 5)
    );
    assert_eq!(lease.as_slice(), source);
    {
        let pending = (lease.try_append(b"12345")).expect("admit the exact suffix");
        assert_eq!(pending.as_slice(), source);
    }
    assert_eq!(lease.as_slice(), source);
    lease
        .try_append(b"12345")
        .map(|append| append.commit())
        .as_ref()
        .expect("required fixture outcome");
    assert_eq!(lease.as_slice(), b"abc12345");
    assert_eq!(lease.as_slice().as_ptr(), address);
    lease
        .try_append(b"")
        .map(|append| append.commit())
        .as_ref()
        .expect("required fixture outcome");
    lease.truncate(3);
    lease
        .try_append(b"!")
        .map(|append| append.commit())
        .as_ref()
        .expect("required fixture outcome");
    lease.as_mut_slice().make_ascii_uppercase();
    assert_eq!(lease.as_slice(), b"ABC!");
}

#[test]
fn retains_prefix_sources() {
    let mut lease = (pool::Span::try_new(8)).expect("allocate a prefix destination");
    let first = b"ab".as_slice();
    let second = b"cdef".as_slice();
    let (admitted, activity) =
        Tracker::activity(|| pool::Append::try_prefixed(&mut lease, *b"<>!", [first, second]));
    assert!(activity == Activity::default());
    let result = admitted;
    let error = result
        .err()
        .expect("reject the complete nine-byte operation");
    assert_eq!(
        error.into_parts(),
        (NonZero::<usize>::MIN.saturating_add(8), 8)
    );
    assert_eq!(lease.len(), 0);
    let append = (pool::Append::try_prefixed(&mut lease, *b"<>", [first, second]))
        .expect("admit prefix and both immutable sources");
    let ((lease, sources), activity) = Tracker::activity(|| append.commit());
    assert!(activity == Activity::default());
    assert_eq!(sources[0].as_ptr(), first.as_ptr());
    assert_eq!(sources[1].as_ptr(), second.as_ptr());
    assert_eq!(lease.as_slice(), b"<>abcdef");
}

#[test]
fn copies_fixed_sources() {
    let layout = pool::Width::<16>::new(1).expect("one fixed-width slot");
    let pool = pool::Pool::<state::Initialized, owners::Plain<_>>::try_from(layout)
        .expect("allocate the fixed-width pool");
    let (lease, activity) = Tracker::activity(|| {
        let lease = pool
            .copy([b"abcd", b"efgh", b"ijkl", b"mnop"])
            .expect("the fixed-width slot is available");
        assert_eq!(lease.as_slice(), b"abcdefghijklmnop");
        let pointer = lease.as_slice().as_ptr();
        assert!(pool.copy([b"next"; 4]).is_none());
        drop(lease);
        let reused = pool
            .copy([b"next"; 4])
            .expect("the fixed-width slot was returned");
        assert_eq!(reused.as_slice().as_ptr(), pointer);
        reused
    });
    assert!(activity == Activity::default());
    let ((), activity) = Tracker::activity(|| drop(pool));
    assert!(activity == Activity::default());
    assert_eq!(lease.as_slice(), b"nextnextnextnext");
}

#[test]
fn retains_exact_output() {
    let pool =
        (pool::Pool::<state::Initialized>::try_new(1, 8)).expect("allocate initialized output");
    let mut lease = (pool.acquire()).expect("acquire the initialized slot");
    try_fill::<4, _>(lease.output(), |bytes| bytes.copy_from_slice(b"comp"))
        .expect("fixture write fits");
    let ((), activity) = Tracker::activity(|| {
        try_fill::<4, _>(lease.output(), |bytes| bytes.copy_from_slice(b"lete"))
            .expect("fixture write fits");
    });
    assert!(activity == Activity::default());
    assert_eq!(lease.as_slice(), b"complete");
    lease.as_mut_slice().make_ascii_uppercase();
    assert_eq!(lease.as_slice(), b"COMPLETE");
    assert_eq!(lease.output().len(), 0);
}

#[test]
fn lease_outlives_pool() {
    let mut lease = {
        let pool = (pool::Pool::<state::Initialized>::try_new(1, 8))
            .expect("the initialized detached-lease pool must allocate");

        (pool.acquire()).expect("acquire the initialized slot")
    };
    try_fill::<4, _>(lease.output(), |bytes| bytes.copy_from_slice(b"body"))
        .expect("fixture write fits");
    assert_eq!(lease.as_slice(), b"body");
}

#[test]
fn keeps_initialized_bytes() {
    assert_eq!(
        std::mem::size_of::<pool::Pool<state::Initialized>>(),
        std::mem::size_of::<pool::Pool>(),
    );
    let pool = (pool::Pool::<state::Initialized>::try_new(1, 8))
        .expect("the initialized-byte reuse pool must allocate");
    {
        let mut lease = (pool.acquire()).expect("acquire the initialized slot");
        assert_eq!(lease.output().as_mut_slice(), &[0; 8]);
        try_fill::<4, _>(lease.output(), |bytes| {
            bytes.copy_from_slice(b"body");
            bytes.make_ascii_uppercase();
        })
        .expect("fixture write fits");
        assert_eq!(lease.as_slice(), b"BODY");
    }

    let mut reused = (pool.acquire()).expect("reacquire the initialized slot");
    assert_eq!(&reused.output().as_mut_slice()[..4], b"BODY");
    try_fill::<3, _>(reused.output(), |bytes| bytes.copy_from_slice(b"new"))
        .expect("fixture write fits");
    let frozen = reused.freeze();
    assert_eq!(frozen.as_ref(), b"new");
    assert!(pool.acquire().is_none());
    drop(frozen);
    assert!(pool.acquire().is_some());
}

fn release<S: state::State, L>(pool: pool::Pool<S, owners::Plain<L>>, bytes: usize) {
    let (slots, lease, closed) = {
        let pool = pool;
        let slots = pool.slots();
        let (lease, hot) = Tracker::activity(|| pool.acquire());
        assert!(hot == Activity::default());
        assert_eq!(lease.is_some(), slots != 0);
        (slots, lease, Tracker::begin())
    };
    let closed = closed.finish();
    let released = match lease {
        Some(lease) => {
            let released = {
                let lease = lease;
                assert!(closed == Activity::default());
                assert!(lease.is_empty());
                Tracker::begin()
            };
            released.finish()
        }
        None => {
            assert_eq!(slots, 0);
            closed
        }
    };
    assert_eq!(released.allocations, 0);
    assert_eq!(released.deallocations, 1);
    assert_eq!(released.deallocated_bytes, bytes);
}

#[test]
fn dynamic_geometry() {
    for (slots, capacity) in [(0, 1), (1, 1), (2, 3), (3, 17), (5, 127), (11, 4093)] {
        let layout = (pool::Layout::new(slots, capacity)).expect("valid pool geometry");
        let (created, build) = Tracker::activity(|| pool::Pool::<state::Uninit>::try_from(layout));
        let pool = (created).expect("pool allocation");
        assert_eq!(pool.slots(), slots);
        assert_eq!(pool.capacity().get() as usize, capacity);
        assert_eq!(pool.available(), slots);
        assert_eq!(build.allocations, 1);
        assert_eq!(build.deallocations, 0);
        release(pool, build.allocated_bytes);
    }
}

fn fixed<const N: usize, const C: usize>() {
    let layout = pool::Fixed::<N, C>::new();
    let (created, build) = Tracker::activity(|| {
        pool::Pool::<state::Initialized, owners::Plain<pool::Fixed<N, C>>>::try_from(layout)
    });
    let pool = (created).expect("fixed pool allocation");
    assert_eq!(build.allocations, 1);
    assert_eq!(build.deallocations, 0);
    release(pool, build.allocated_bytes);
}

#[test]
fn static_geometry() {
    fixed::<0, 1>();
    fixed::<1, 3>();
    fixed::<7, 17>();
    let layout = (pool::Width::<13>::new(5)).expect("valid static width");
    let (created, build) = Tracker::activity(|| {
        pool::Pool::<state::Uninit, owners::Plain<pool::Width<13>>>::try_from(layout)
    });
    let pool = (created).expect("width pool allocation");
    assert_eq!(build.allocations, 1);
    release(pool, build.allocated_bytes);
}

#[test]
fn rejects_before_alloc() {
    let (result, trace) = Tracker::activity(|| {
        pool::Pool::<state::Uninit>::try_new(u32::MAX as usize, u32::MAX as usize)
    });
    assert!(matches!(
        result,
        Err(pool::CreateError::Layout(
            pool::LayoutError::PlainDataArray {
                slots: u32::MAX,
                capacity: u32::MAX,
            }
        ))
    ));
    assert!(trace == Activity::default());
    let (result, trace) = Tracker::activity(|| pool::Pool::<state::Uninit>::try_new(1, 0));
    assert!(matches!(
        result,
        Err(pool::CreateError::Layout(pool::LayoutError::ZeroCapacity))
    ));
    assert!(trace == Activity::default());
    let (result, trace) = Tracker::activity(|| pool::Pool::<state::Uninit>::try_new(usize::MAX, 1));
    assert!(matches!(
        result,
        Err(pool::CreateError::Layout(pool::LayoutError::SlotsTooLarge))
    ));
    assert!(trace == Activity::default());
}

#[test]
fn exhaustion_keeps_failure() {
    let (failed, activity) = Tracker::activity(|| {
        Tracker::fail_after(0, || pool::Pool::<state::Uninit>::try_new(3, 17))
    });
    assert!(matches!(
        failed,
        Err(pool::CreateError::Exhausted(o3::alloc::Exhausted))
    ));
    assert!(activity == Activity::default());
    let (failed, activity) = Tracker::activity(|| {
        Tracker::fail_after(0, || {
            pool::Pool::<state::Initialized, owners::Plain<pool::Fixed<3, 17>>>::try_from(
                pool::Fixed::new(),
            )
        })
    });
    assert!(matches!(failed, Err(o3::alloc::Exhausted)));
    assert!(activity == Activity::default());
}