bufferring 0.0.2

Ring buffers for Rust
Documentation
//! Utilities for property-based testing with [`MaskingBuffer`].

#![cfg(feature = "proptest")]

use core::fmt::Debug;
use core::num::{NonZeroUsize, Wrapping};
use core::ops::Range;

extern crate alloc;
use alloc::vec::Vec;

use proptest::prelude::*;
use proptest::num::usize::{Any as AnyUsize, ANY as ANY_USIZE};
use proptest::strategy::Map as PropMap;

use crate::RingBuffer;
use crate::prop::BufferStrategy;
use crate::storage::{ArrayStorage, HeapStorage};

use super::MaskingBuffer;

/// The ability to generate [`MaskingBuffer`]s using [`ArrayStorage`] for
/// property-based testing.
impl<T, const N: usize> Arbitrary for MaskingBuffer<ArrayStorage<T, N>>
where T: Arbitrary + Debug {
    type Parameters = T::Parameters;
    type Strategy = ArrayStrategy<T::Strategy, N>;

    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
        arb_array(T::arbitrary_with(args))
    }
}

/// The ability to generate [`MaskingBuffer`]s using [`HeapStorage`] for
/// property-based testing.
impl<T: Arbitrary + Debug> Arbitrary for MaskingBuffer<HeapStorage<T>> {
    type Parameters = T::Parameters;
    type Strategy = HeapStrategy<T::Strategy>;

    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
        arb_heap(T::arbitrary_with(args))
    }
}

/// The [`Strategy`] type for generating [`MaskingBuffer`]s using
/// [`ArrayStorage`].
pub type ArrayStrategy<T, const N: usize> = PropMap<
    (AnyUsize, BufferStrategy<Just<NonZeroUsize>, T>),
    fn (
        (usize, (NonZeroUsize, Vec<<T as Strategy>::Value>))
    ) -> MaskingBuffer<ArrayStorage<<T as Strategy>::Value, N>>,
>;

/// A [`Strategy`] for [`MaskingBuffer`]s using [`ArrayStorage`].
pub fn arb_array<T: Strategy, const N: usize>(
    arb_item: T
) -> ArrayStrategy<T, N> {
    fn mapper<T: Strategy, const N: usize>(
        (cur, (cap, items)): (usize, (NonZeroUsize, Vec<T::Value>))
    ) -> MaskingBuffer<ArrayStorage<T::Value, N>> {
        assert_eq!(cap.get(), N);
        let mut buffer = MaskingBuffer {
            cur: Wrapping(cur), len: 0,
            data: ArrayStorage::new(),
        };
        for item in items.into_iter() {
            assert!(buffer.enqueue(item).is_none());
        }
        buffer
    }

    assert!(N.is_power_of_two(),
        "`MaskingBuffer` requires a power-of-two capacity!");
    let capacity = Just(NonZeroUsize::new(N).unwrap());
    (ANY_USIZE, BufferStrategy::new(capacity, arb_item))
        .prop_map(mapper::<T, N> as _)
}

/// A [`Strategy`] for [`MaskingBuffer`]s using [`ArrayStorage`], also returning
/// the elements that the buffer currently contains.
pub fn arb_array_paired<T: Strategy, const N: usize>(
    arb_item: T
) -> impl Strategy<Value = (
    MaskingBuffer<ArrayStorage<T::Value, N>>,
    Vec<T::Value>,
)> where T::Value: Clone {
    assert!(N.is_power_of_two(),
        "`MaskingBuffer` requires a power-of-two capacity!");
    let capacity = Just(NonZeroUsize::new(N).unwrap());
    (ANY_USIZE, BufferStrategy::new(capacity, arb_item))
    .prop_map(|(cur, (cap, items))| {
        assert_eq!(cap.get(), N);
        let mut buffer = MaskingBuffer {
            cur: Wrapping(cur), len: 0,
            data: ArrayStorage::new(),
        };
        for item in items.iter() {
            assert!(buffer.enqueue(item.clone()).is_none());
        }
        (buffer, items)
    })
}

/// A [`Strategy`] for empty [`MaskingBuffer`]s using [`ArrayStorage`].
pub fn arb_array_empty<T: Debug, const N: usize>(
) -> impl Strategy<Value = MaskingBuffer<ArrayStorage<T, N>>> {
    ANY_USIZE.prop_map(|cur| MaskingBuffer {
        cur: Wrapping(cur),
        len: 0,
        data: ArrayStorage::new(),
    })
}

/// The [`Strategy`] type for generating [`MaskingBuffer`]s using
/// [`HeapStorage`].
pub type HeapStrategy<T> = PropMap<
    (AnyUsize, BufferStrategy<PropMap<Range<u32>, fn(u32) -> NonZeroUsize>, T>),
    fn (
        (usize, (NonZeroUsize, Vec<<T as Strategy>::Value>))
    ) -> MaskingBuffer<HeapStorage<<T as Strategy>::Value>>,
>;

/// A [`Strategy`] for [`MaskingBuffer`]s using [`HeapStorage`].
pub fn arb_heap<T: Strategy>(arb_item: T) -> HeapStrategy<T> {
    fn mapper<T: Strategy>(
        (cur, (cap, items)): (usize, (NonZeroUsize, Vec<T::Value>))
    ) -> MaskingBuffer<HeapStorage<T::Value>> {
        let mut buffer = MaskingBuffer {
            cur: Wrapping(cur), len: 0,
            data: HeapStorage::with_capacity(cap),
        };
        for item in items.into_iter() {
            assert!(buffer.enqueue(item).is_none());
        }
        buffer
    }

    fn capacity_mapper(exp: u32) -> NonZeroUsize {
        NonZeroUsize::new(1usize << exp).unwrap()
    }

    let capacity = (0u32 .. 14u32).prop_map(capacity_mapper as _);
    (ANY_USIZE, BufferStrategy::new(capacity, arb_item))
        .prop_map(mapper::<T> as _)
}

/// A [`Strategy`] for [`MaskingBuffer`]s using [`ArrayStorage`], also returning
/// the elements that the buffer currently contains.
pub fn arb_heap_paired<T: Strategy>(
    arb_item: T
) -> impl Strategy<Value = (
    MaskingBuffer<HeapStorage<T::Value>>,
    Vec<T::Value>,
)> where T::Value: Clone {
    fn capacity_mapper(exp: u32) -> NonZeroUsize {
        NonZeroUsize::new(1usize << exp).unwrap()
    }

    let capacity = (0u32 .. 14u32).prop_map(capacity_mapper);
    (ANY_USIZE, BufferStrategy::new(capacity, arb_item))
    .prop_map(|(cur, (cap, items))| {
        let mut buffer = MaskingBuffer {
            cur: Wrapping(cur), len: 0,
            data: HeapStorage::with_capacity(cap),
        };
        for item in items.iter() {
            assert!(buffer.enqueue(item.clone()).is_none());
        }
        (buffer, items)
    })
}

/// A [`Strategy`] for empty [`MaskingBuffer`]s using [`HeapStorage`].
pub fn arb_heap_empty<T: Debug>(
) -> impl Strategy<Value = MaskingBuffer<HeapStorage<T>>> {
    (ANY_USIZE, (0u32 .. 14u32)).prop_map(|(cur, cap)| MaskingBuffer {
        cur: Wrapping(cur),
        len: 0,
        data: HeapStorage::with_capacity(
            NonZeroUsize::new(1usize << cap).unwrap()),
    })
}