forkable 0.1.0

Fork-able iterators and asynchronous streams.
Documentation
use core::{
    cell::UnsafeCell, iter::{
        FusedIterator, Iterator
    }
};


pub mod nucleus;

use nucleus::Nucleus;

use crate::maybe::MaybeRc;

/// A fully non-allocating [`Iterator`] wrapper that allows forking.
pub struct Forkable<I, const S: usize>
where
    I: Iterator + FusedIterator,
    <I as Iterator>::Item: Clone + Copy

{
    iter_index: usize,
    iter: MaybeRc<UnsafeCell<Nucleus<I, S>>>
}

impl<I, const S: usize> Iterator for Forkable<I, S>
where
    I: Iterator + FusedIterator,
    <I as Iterator>::Item: Clone + Copy
{
    type Item = <I as Iterator>::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let Self {
            iter_index,
            ref iter
        } = self;

        // SAFETY: Guaranteed to be valid
        let iter = unsafe {
            iter
                .get()
                .as_mut()
                .unwrap_unchecked()
        };

        if *iter_index < iter.oldest_index() {
            *iter_index = iter_index.saturating_add(1);

            return None
        }

        let target_value = match iter.at(iter_index.saturating_sub(iter.oldest_index())) {
            target_value @ Some(_) => target_value,
            None => {
                /*
                    Internal iterator is not exposed as public API
                    So if an index not stored at this point was not found, it must be the next one.
                */
                iter.next()
            }
        };

        

        *iter_index = iter_index.saturating_add(1);

        target_value
    }
}

impl<I, const S: usize> Forkable<I, S>
where
    I: Iterator + FusedIterator,
    <I as Iterator>::Item: Clone + Copy,
{
    #[inline]
    pub fn new(target_iterator: I) -> Self {
        let iter_index = 0;

        let iter = MaybeRc::from(
            UnsafeCell::new(
                Nucleus::new(target_iterator)
            )
        );

        Self {
            iter_index,
            iter
        }
    }

    #[inline]
    pub fn is_exhausted(&self) -> bool {
        let Self {
            iter_index,
            ref iter
        } = self;

        // SAFETY: Guaranteed to be valid
        let iter = unsafe {
            iter
                .get()
                .as_ref()
                .unwrap_unchecked()
        };

        return *iter_index < iter.oldest_index()
    }

    #[inline]
    pub fn fork(self) -> (Self, Self) {
        let iter_index = self.iter_index;
        let (i0, i1) = self.iter.make_clone();
    
        (
            Self {
                iter_index,
                iter: i0
            },
            Self {
                iter_index,
                iter: i1
            }
        )
    }
}

#[cfg(test)]
mod test {
    use super::Forkable;

    macro_rules! assert_matches {
        ($($target_tt:tt)*) => {
            assert!(
                matches!(
                    $($target_tt)*
                )
            )
        };
    }

    #[test]
    fn storage_exhaust() {
        const INITIAL_VALUE: usize = 0x00;
        const STORAGE_SIZE: usize = 0x10;
    
        let f: Forkable<_, STORAGE_SIZE> = Forkable::new((INITIAL_VALUE..).into_iter());

        let (mut f0, mut f1) = f.fork();

        for target_index in INITIAL_VALUE..STORAGE_SIZE * 2 {
            assert_matches!(f0.next(), Some(target_value) if target_value == target_index)
        }

        for _ in INITIAL_VALUE..STORAGE_SIZE {
            assert_matches!(f1.next(), None)
        }
    }

    #[test]
    fn iter_race() {
        const INITIAL_VALUE: usize = 0x00;
        const STORAGE_SIZE: usize = 0x10;
        const OVERTAKE_COUNT: usize = STORAGE_SIZE / 2;
    
        let f: Forkable<_, STORAGE_SIZE> = Forkable::new((INITIAL_VALUE..).into_iter());

        let (mut f0, mut f1) = f.fork();

        for _ in 0..=OVERTAKE_COUNT {
            assert!(matches!(f0.next(), Some(_)));
        }

        for target_index in INITIAL_VALUE..STORAGE_SIZE {
            assert_matches!(f1.next(), Some(target_value) if target_value == target_index)
        }
    }
}