acyclic-fs 0.1.5

Rust SDK for versioned workspaces and embedded native mounts
Documentation
use super::*;
use crate::foundation::Digest;
use crate::storage::ObjectKind;

fn object(suffix: u8) -> ObjectId {
    let mut digest = [0_u8; 32];
    digest[8] = suffix;
    ObjectId {
        kind: ObjectKind::ExtentPage,
        digest: Digest::from_bytes(digest),
    }
}

#[test]
fn simultaneous_claims_produce_one_operation_wide_peak() -> Result<(), AllocationError> {
    let mut ledger = AllocationLedger::default();
    let mut work = WorkCounters::default();
    let first = ledger.claim_elements::<u64>(8, &mut work, WorkBudget::UNBOUNDED)?;
    let second = ledger.claim_elements::<u32>(8, &mut work, WorkBudget::UNBOUNDED)?;
    assert_eq!(work.peak_allocation_bytes, 96);
    assert_eq!(work.allocation_operations, 2);
    ledger.release(first)?;
    ledger.release(second)?;
    assert_eq!(ledger.live_bytes(), 0);
    assert_eq!(work.peak_allocation_bytes, 96);
    Ok(())
}

#[test]
fn one_byte_short_budget_rejects_before_claim() -> Result<(), AllocationError> {
    let mut ledger = AllocationLedger::default();
    let mut work = WorkCounters::default();
    let error = ledger
        .claim_elements::<u64>(
            8,
            &mut work,
            WorkBudget {
                peak_allocation_bytes: 63,
                allocation_operations: 1,
                ..WorkBudget::UNBOUNDED
            },
        )
        .err()
        .ok_or(AllocationError::AllocationFailed)?;
    assert!(matches!(
        error,
        AllocationError::Work(WorkError::BudgetExceeded {
            counter: "peak_allocation_bytes",
            observed: 64,
            maximum: 63,
        })
    ));
    assert_eq!(ledger.live_bytes(), 0);
    assert_eq!(work, WorkCounters::default());
    Ok(())
}

#[test]
fn vector_capacity_and_ledger_corruption_guards_are_total() -> Result<(), AllocationError> {
    let mut capacity = LogicalVecCapacity::default();
    let mut values = Vec::<u64>::new();
    let mut ledger = AllocationLedger::default();
    let mut work = WorkCounters::default();
    capacity.ensure_for_push(
        &mut values,
        1,
        &mut ledger,
        &mut work,
        WorkBudget::UNBOUNDED,
    )?;
    let admitted = ledger.live_bytes();
    capacity.ensure_for_push(
        &mut values,
        1,
        &mut ledger,
        &mut work,
        WorkBudget::UNBOUNDED,
    )?;
    assert_eq!(ledger.live_bytes(), admitted);
    values.push(1);
    assert_eq!(
        capacity.ensure_for_push(
            &mut values,
            1,
            &mut ledger,
            &mut work,
            WorkBudget::UNBOUNDED,
        ),
        Err(AllocationError::CapacityExceeded)
    );
    assert_eq!(
        ledger.release(admitted + 1),
        Err(AllocationError::ReleaseInvariant)
    );
    ledger.release(admitted)?;

    let mut overflow = AllocationLedger::default();
    overflow.claim_bytes(u64::MAX, 0, &mut work, WorkBudget::UNBOUNDED)?;
    assert_eq!(
        overflow.claim_bytes(1, 0, &mut work, WorkBudget::UNBOUNDED),
        Err(AllocationError::Overflow)
    );
    assert_eq!(
        VisitedObjectSet::slot_count(usize::MAX),
        Err(AllocationError::Overflow)
    );
    Ok(())
}

#[test]
fn visited_set_is_deterministic_bounded_and_detects_aliases() -> Result<(), AllocationError> {
    let mut ledger = AllocationLedger::default();
    let mut work = WorkCounters::default();
    let mut visited = VisitedObjectSet::new(2, &mut ledger, &mut work, WorkBudget::UNBOUNDED)?;
    let first = visited.insert(object(1), &mut ledger, &mut work, WorkBudget::UNBOUNDED)?;
    let collision = visited.insert(object(2), &mut ledger, &mut work, WorkBudget::UNBOUNDED)?;
    let alias = visited.insert(object(1), &mut ledger, &mut work, WorkBudget::UNBOUNDED)?;
    assert_eq!(first.probes, 1);
    assert_eq!(collision.probes, 2);
    assert!(!alias.inserted);
    assert_eq!(alias.probes, 1);
    assert_eq!(
        visited.insert(object(3), &mut ledger, &mut work, WorkBudget::UNBOUNDED,),
        Err(AllocationError::CapacityExceeded)
    );
    visited.release(&mut ledger)?;
    assert_eq!(ledger.live_bytes(), 0);

    let mut scalable_ledger = AllocationLedger::default();
    let mut scalable_work = WorkCounters::default();
    let mut scalable = VisitedObjectSet::new(
        1_024,
        &mut scalable_ledger,
        &mut scalable_work,
        WorkBudget::UNBOUNDED,
    )?;
    let initial_peak = scalable_work.peak_allocation_bytes;
    for suffix in 0..32 {
        assert!(
            scalable
                .insert(
                    object(suffix),
                    &mut scalable_ledger,
                    &mut scalable_work,
                    WorkBudget::UNBOUNDED,
                )?
                .inserted
        );
    }
    let eager_maximum_bytes = 2_u64
        .checked_mul(1_024)
        .and_then(|slots| slots.checked_mul(u64::try_from(size_of::<Option<ObjectId>>()).ok()?))
        .ok_or(AllocationError::Overflow)?;
    assert!(initial_peak < eager_maximum_bytes);
    assert!(scalable_work.peak_allocation_bytes < eager_maximum_bytes);
    assert!(scalable_work.allocation_operations > 1);
    assert!(scalable_work.bytes_copied > 0);
    scalable.release(&mut scalable_ledger)?;
    assert_eq!(scalable_ledger.live_bytes(), 0);
    Ok(())
}

#[test]
fn visited_growth_failures_release_candidate_storage_and_preserve_the_old_table()
-> Result<(), AllocationError> {
    let mut empty_ledger = AllocationLedger::default();
    let mut empty_work = WorkCounters::default();
    assert!(matches!(
        VisitedObjectSet::new(0, &mut empty_ledger, &mut empty_work, WorkBudget::UNBOUNDED,),
        Err(AllocationError::InvalidCapacity)
    ));
    assert_eq!(empty_ledger.live_bytes(), 0);

    for fail_on_copy in [false, true] {
        let mut ledger = AllocationLedger::default();
        let mut work = WorkCounters::default();
        let mut visited = VisitedObjectSet::new(16, &mut ledger, &mut work, WorkBudget::UNBOUNDED)?;
        for suffix in 0..8 {
            assert!(
                visited
                    .insert(
                        object(suffix),
                        &mut ledger,
                        &mut work,
                        WorkBudget::UNBOUNDED,
                    )?
                    .inserted
            );
        }
        let stable_bytes = ledger.live_bytes();
        let mut budget = WorkBudget::UNBOUNDED;
        if fail_on_copy {
            budget.bytes_copied = work.bytes_copied;
        } else {
            budget.items_examined = work.items_examined.saturating_add(1);
        }
        let failure = visited
            .insert(object(9), &mut ledger, &mut work, budget)
            .err()
            .ok_or(AllocationError::AllocationFailed)?;
        assert!(matches!(failure, AllocationError::Work(_)));
        assert_eq!(ledger.live_bytes(), stable_bytes);
        assert!(
            !visited
                .insert(object(0), &mut ledger, &mut work, WorkBudget::UNBOUNDED,)?
                .inserted
        );
        visited.release(&mut ledger)?;
        assert_eq!(ledger.live_bytes(), 0);
    }
    Ok(())
}