#![allow(clippy::needless_bitwise_bool)]
use bytemuck::{Pod, Zeroable};
use rostl_primitives::{
cmov_body, cxchg_body, impl_cmov_for_generic_pod, indexable::Length, traits::Cmov,
traits::_Cmovbase,
};
use crate::array::ShortArray;
#[repr(C)]
#[derive(Debug, Default, Clone, Copy, Zeroable)]
pub struct ShortQueueElement<T>
where
T: Cmov + Pod,
{
timestamp: usize,
pub(crate) value: T,
}
unsafe impl<T: Cmov + Pod> Pod for ShortQueueElement<T> {}
impl_cmov_for_generic_pod!(ShortQueueElement<T>; where T: Cmov + Pod);
impl<T> ShortQueueElement<T>
where
T: Cmov + Pod,
{
pub const fn is_empty(&self) -> bool {
self.timestamp == 0
}
}
#[derive(Debug)]
pub struct ShortQueue<T, const N: usize>
where
T: Cmov + Pod,
{
highest_timestamp: usize,
lowest_timestamp: usize,
pub(crate) size: usize,
pub(crate) elements: ShortArray<ShortQueueElement<T>, N>,
}
impl<T, const N: usize> ShortQueue<T, N>
where
T: Cmov + Pod + Default,
{
pub fn new() -> Self {
Self { highest_timestamp: 0, lowest_timestamp: 1, size: 0, elements: ShortArray::new() }
}
pub fn maybe_push(&mut self, real: bool, element: T) {
debug_assert!(!real | (self.size < N));
self.size.cmov(&(self.size + 1), real);
self.highest_timestamp.cmov(&(self.highest_timestamp + 1), real);
let mut inserted = !real;
let mut lowest_timestamp = self.highest_timestamp;
for i in 0..self.elements.len() {
let curr = &mut self.elements.data[i];
let is_empty = curr.is_empty();
let should_insert = !inserted & is_empty;
let is_lowest_timemstamp = !is_empty & (curr.timestamp < lowest_timestamp);
curr.timestamp.cmov(&self.highest_timestamp, should_insert);
curr.value.cmov(&element, should_insert);
lowest_timestamp.cmov(&curr.timestamp, is_lowest_timemstamp);
inserted |= should_insert;
}
debug_assert!(inserted);
self.lowest_timestamp.cmov(&lowest_timestamp, real);
}
pub fn maybe_pop(&mut self, real: bool, out: &mut T) {
debug_assert!(!real | (self.size > 0));
self.size.cmov(&(self.size.wrapping_sub(1)), real);
let mut second_lowest_timestamp = self.highest_timestamp;
for i in 0..self.elements.len() {
let curr = &mut self.elements.data[i];
let is_lowest = curr.timestamp == self.lowest_timestamp;
let could_be_second_lowest =
!curr.is_empty() & !is_lowest & (curr.timestamp < second_lowest_timestamp);
let should_pop = real & is_lowest;
second_lowest_timestamp.cmov(&curr.timestamp, could_be_second_lowest);
out.cmov(&curr.value, should_pop);
curr.timestamp.cmov(&0, should_pop);
}
self.lowest_timestamp.cmov(&second_lowest_timestamp, real);
}
}
impl<T, const N: usize> Length for ShortQueue<T, N>
where
T: Cmov + Pod + Default,
{
fn len(&self) -> usize {
self.size
}
}
impl<T, const N: usize> Default for ShortQueue<T, N>
where
T: Cmov + Pod + Default,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_short_queue() {
let mut queue: ShortQueue<u32, 3> = ShortQueue::new();
assert_eq!(queue.len(), 0);
queue.maybe_push(true, 1); assert_eq!(queue.len(), 1);
queue.maybe_push(true, 2); assert_eq!(queue.len(), 2);
queue.maybe_push(false, 42);
assert_eq!(queue.len(), 2);
queue.maybe_push(true, 3); assert_eq!(queue.len(), 3);
queue.maybe_push(false, 4);
assert_eq!(queue.len(), 3);
let mut out = 0;
queue.maybe_pop(true, &mut out); assert_eq!(out, 1);
assert_eq!(queue.len(), 2);
queue.maybe_pop(true, &mut out); assert_eq!(out, 2);
assert_eq!(queue.len(), 1);
queue.maybe_pop(false, &mut out);
assert_eq!(queue.len(), 1);
queue.maybe_pop(true, &mut out); assert_eq!(out, 3);
assert_eq!(queue.len(), 0);
queue.maybe_pop(false, &mut out);
assert_eq!(queue.len(), 0);
}
}