use crate::util::lock::Lock;
use crate::{InterruptControl, NoInterruptControl};
use core::cell::UnsafeCell;
pub(crate) struct Lifo<const N: usize, I: InterruptControl = NoInterruptControl> {
lock: Lock<I>,
len: UnsafeCell<usize>,
buf: UnsafeCell<[usize; N]>,
}
impl<const N: usize, I: InterruptControl> Lifo<N, I> {
pub(crate) const fn new() -> Self {
Self {
lock: Lock::new(),
len: UnsafeCell::new(0),
buf: UnsafeCell::new([0; N]),
}
}
#[inline]
pub(crate) fn lock(&self) -> LifoGuard<'_, N, I> {
self.lock.acquire();
LifoGuard { lifo: self }
}
}
pub(crate) struct LifoGuard<'a, const N: usize, I: InterruptControl> {
lifo: &'a Lifo<N, I>,
}
impl<const N: usize, I: InterruptControl> LifoGuard<'_, N, I> {
#[inline]
pub(crate) fn len(&self) -> usize {
unsafe { *self.lifo.len.get() }
}
#[inline]
pub(crate) fn is_full(&self) -> bool {
self.len() >= N
}
#[inline]
pub(crate) fn pop(&mut self) -> Option<usize> {
let len = unsafe { &mut *self.lifo.len.get() };
if *len == 0 {
return None;
}
*len -= 1;
Some(unsafe { (*self.lifo.buf.get())[*len] })
}
#[inline]
pub(crate) fn push(&mut self, addr: usize) {
let len = unsafe { &mut *self.lifo.len.get() };
debug_assert!(*len < N, "push into a full Lifo");
unsafe { (*self.lifo.buf.get())[*len] = addr };
*len += 1;
}
#[inline]
pub(crate) fn push_slice(&mut self, src: &[usize]) -> usize {
let len = unsafe { &mut *self.lifo.len.get() };
let buf = unsafe { &mut *self.lifo.buf.get() };
let take = (N - *len).min(src.len());
for &addr in src.iter().take(take) {
buf[*len] = addr;
*len += 1;
}
take
}
#[inline]
pub(crate) fn take_top(&mut self, n: usize) -> &[usize] {
let len = unsafe { &mut *self.lifo.len.get() };
debug_assert!(n <= *len, "take_top past len");
let start = *len - n;
*len = start;
let buf = unsafe { &*self.lifo.buf.get() };
&buf[start..start + n]
}
}
impl<const N: usize, I: InterruptControl> Drop for LifoGuard<'_, N, I> {
#[inline]
fn drop(&mut self) {
self.lifo.lock.release();
}
}