use std::{cell, marker, mem, ptr};
pub use bounds::Bounds;
use crate::{
alloc::array,
depot::{
capacity,
queue::{indexed::shared, links},
},
};
mod bounds;
pub struct Fifo<T: Copy, P = (), const N: usize = 1> {
pub(in crate::depot) nodes: array::Block<Node<T, P, N>, capacity::Count, [links::Chain; N]>,
}
impl<T: Copy, P: Default, const N: usize> Fifo<T, P, N> {
pub fn try_new(capacity: usize) -> Result<Self, crate::alloc::Error> {
Self::try_with(capacity, P::default)
}
}
impl<T: Copy, P, const N: usize> Fifo<T, P, N> {
pub fn try_with(
capacity: usize,
mut seed: impl FnMut() -> P,
) -> Result<Self, crate::alloc::Error> {
const { assert!(N > 0) };
let count = capacity::Count::try_count(capacity, crate::alloc::Error::Overflow)?;
let nodes =
array::Block::try_new(count, [const { links::Chain::new() }; N])?.init(|index| {
let index = unsafe { links::Index::new_usize(index) };
Node {
state: seed(),
values: [const { cell::Cell::new(mem::MaybeUninit::uninit()) }; N],
hooks: [(); N].map(|()| links::Hook::new(index)),
}
});
Ok(Self { nodes })
}
}
impl<T: Copy, P, const N: usize> Fifo<T, P, N> {
pub fn capacity(&self) -> usize {
self.nodes.extent().get()
}
pub fn is_empty<const Q: usize>(&self) -> bool {
const { assert!(Q < N) };
self.nodes.state()[Q].first().is_none()
}
pub fn iter(&self) -> impl Iterator<Item = shared::Slot<'_, T, P, N>> {
self.nodes
.iter()
.map(|node| shared::Slot { fifo: self, node })
}
pub fn get(&self, index: usize) -> Option<shared::Slot<'_, T, P, N>> {
let node = self.nodes.get(index)?;
Some(shared::Slot { fifo: self, node })
}
pub fn try_entry(&self, index: usize) -> Result<shared::Slot<'_, T, P, N>, shared::Bounds> {
self.get(index).ok_or(shared::Bounds {
index,
capacity: self.capacity(),
})
}
pub fn front<const Q: usize>(&self) -> Option<T> {
Some(self.first::<Q>()?.value())
}
pub fn next<const Q: usize>(&self) -> Option<(shared::Slot<'_, T, P, N>, T)> {
Some(self.first::<Q>()?.remove())
}
fn first<const Q: usize>(&self) -> Option<Member<'_, T, P, N, Q, Head>> {
const { assert!(Q < N) };
let index = self.nodes.state()[Q].first()?;
let node = unsafe { self.nodes.get_unchecked(index.get()) };
Some(Member {
slot: shared::Slot { fifo: self, node },
index,
phase: marker::PhantomData,
})
}
}
pub struct Slot<'a, T: Copy, P = (), const N: usize = 1> {
pub(in crate::depot) fifo: &'a Fifo<T, P, N>,
pub(in crate::depot) node: &'a Node<T, P, N>,
}
enum Head {}
struct Member<'a, T: Copy, P, const N: usize, const Q: usize, S = ()> {
slot: Slot<'a, T, P, N>,
index: links::Index,
phase: marker::PhantomData<S>,
}
impl<T: Copy, P, const N: usize, const Q: usize, S> Member<'_, T, P, N, Q, S> {
fn value(&self) -> T {
unsafe { self.slot.node.values[Q].get().assume_init() }
}
}
impl<'a, T: Copy, P, const N: usize, const Q: usize> Member<'a, T, P, N, Q, Head> {
fn remove(self) -> (Slot<'a, T, P, N>, T) {
let value = self.value();
let fifo = self.slot.fifo;
let hooks = Hooks::<_, _, N, Q>(fifo);
crate::depot::raw::Removal::commit(unsafe {
crate::depot::raw::Pop::at(&fifo.nodes.state()[Q], &hooks, self.index)
})
.idle();
(self.slot, value)
}
}
impl<'a, T: Copy, P, const N: usize> Slot<'a, T, P, N> {
pub fn index(&self) -> usize {
self.position().get()
}
pub fn state(&self) -> &'a P {
&self.node.state
}
fn position(&self) -> links::Index {
let index =
unsafe { ptr::from_ref(self.node).offset_from(self.fifo.nodes.as_ptr().cast()) };
unsafe { links::Index::new_usize(index as usize) }
}
pub fn get<const Q: usize>(&self) -> Option<T> {
const { assert!(Q < N) };
let index = self.position();
if !index.is_linked(&self.node.hooks[Q]) {
return None;
}
Some(
Member::<_, _, N, Q> {
slot: Slot {
fifo: self.fifo,
node: self.node,
},
index,
phase: marker::PhantomData,
}
.value(),
)
}
pub fn push<const Q: usize>(&self, value: T) {
const { assert!(Q < N) };
let index = self.position();
let queue = &self.fifo.nodes.state()[Q];
let hooks = Hooks::<_, _, N, Q>(self.fifo);
let change = if index.is_linked(&self.node.hooks[Q]) {
let vacant = unsafe { queue.unlink(&hooks, index) };
unsafe { crate::depot::raw::Insert::<(), false>::from_vacant(queue, &hooks, vacant) }
} else {
unsafe { crate::depot::raw::Insert::<(), false>::new(queue, &hooks, index) }
};
self.node.values[Q].set(mem::MaybeUninit::new(value));
crate::depot::raw::Insert::commit(change);
}
pub fn clear<const Q: usize>(&self) {
const { assert!(Q < N) };
let index = self.position();
if index.is_linked(&self.node.hooks[Q]) {
unsafe {
self.fifo.nodes.state()[Q]
.unlink(&Hooks::<_, _, N, Q>(self.fifo), index)
.idle()
};
}
}
}
pub(in crate::depot) struct Node<T, P, const N: usize> {
state: P,
values: [cell::Cell<mem::MaybeUninit<T>>; N],
hooks: [links::Hook; N],
}
struct Hooks<'a, T: Copy, P, const N: usize, const Q: usize>(&'a Fifo<T, P, N>);
unsafe impl<T: Copy, P, const N: usize, const Q: usize> crate::depot::raw::Hooks
for Hooks<'_, T, P, N, Q>
{
unsafe fn hook_raw(&self, index: links::Index) -> &'_ links::Hook {
const { assert!(Q < N) };
&unsafe { self.0.nodes.get_unchecked(index.get()) }.hooks[Q]
}
}
const _: () = {
assert!(mem::size_of::<Fifo<u64>>() == 24);
assert!(mem::size_of::<Fifo<u64, (), 2>>() == 32);
assert!(mem::size_of::<Slot<'_, u64>>() == 16);
assert!(mem::size_of::<Option<Slot<'_, u64>>>() == 16);
assert!(mem::size_of::<Slot<'_, u64, (), 2>>() == 16);
assert!(mem::size_of::<Option<Slot<'_, u64, (), 2>>>() == 16);
};