bach 0.1.2

Discrete-event simulation environment for async workflows
Documentation
use super::{
    bitset::Bitset,
    entry::{Entry, Queue},
};
use arr_macro::arr;
use core::{fmt, marker::PhantomData};

pub struct Stack<E: Entry> {
    slots: [E::Queue; 256],
    occupied: Bitset,
    current: u8,
    entry: PhantomData<E>,
}

impl<E: Entry> Default for Stack<E> {
    fn default() -> Self {
        Self::new()
    }
}

impl<E: Entry> fmt::Debug for Stack<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Stack")
            .field("occupied", &self.occupied.len())
            .field("current", &self.current)
            .finish()
    }
}

impl<E: Entry> Stack<E> {
    pub fn new() -> Self {
        let slots = arr![E::Queue::new(); 256];
        Self {
            slots,
            occupied: Default::default(),
            current: 0,
            entry: PhantomData,
        }
    }

    pub fn current(&self) -> u8 {
        self.current
    }

    pub fn is_empty(&self) -> bool {
        self.occupied.is_empty()
    }

    pub fn insert(&mut self, index: u8, entry: E) {
        self.occupied.insert(index);
        let list = self.slot_mut(index);
        list.push(entry);
    }

    fn skip(&mut self) {
        if let Some(next) = self.occupied.next_occupied(self.current) {
            self.current = next;
        } else {
            self.current = 0;
        }
    }

    pub fn tick(&mut self, can_skip: bool) -> (E::Queue, bool) {
        self.current = self.current.wrapping_add(1);
        if can_skip {
            self.skip();
        }
        let slot = self.take();
        (slot, self.current == 0)
    }

    pub fn take(&mut self) -> E::Queue {
        let current = self.current;
        self.occupied.remove(current);
        self.slot_mut(current).take()
    }

    #[inline]
    pub fn close<F: FnMut(E)>(&mut self, mut close: F) {
        if self.occupied.is_empty() {
            return;
        }

        let mut idx = 0;
        loop {
            let queue = self.slot_mut(idx);
            while let Some(entry) = queue.pop() {
                close(entry);
            }

            self.occupied.remove(idx);

            if let Some(next_idx) = self.occupied.next_occupied(idx) {
                idx = next_idx;
            } else {
                break;
            }
        }
    }

    #[inline]
    pub fn reset(&mut self) {
        self.current = 0;
    }

    fn slot_mut(&mut self, index: u8) -> &mut E::Queue {
        unsafe { self.slots.get_unchecked_mut(index as usize) }
    }
}