use crate::panic_if_err;
pub struct RingQ<E, const SIZE: usize>
where
E: Clone + Coalesce + Copy
{
pub queue: [Option<E>; SIZE],
pub head: u8,
pub tail: u8
}
pub trait Coalesce : Sized {
fn coalesce(&mut self, with: &Self) -> Result<(),QueueError>;
}
pub trait DoesNotCoalesce {}
pub enum QueueError {
QueueFull,
CannotCoalesce
}
pub trait BoundedMaths<T> {
fn binc<const BOUND: usize>(&mut self);
fn bdec<const BOUND: usize>(&mut self);
fn bsub<const BOUND: usize>(&self, val: T) -> Self;
}
impl BoundedMaths<u8> for u8
{
#[inline(always)]
fn binc<const BOUND: usize>(&mut self) {
*self = (*self + 1) % BOUND as u8;
}
#[inline(always)]
fn bdec<const BOUND: usize>(&mut self) {
if *self > 0 {
*self -= 1;
} else {
*self = (BOUND-1) as u8;
}
}
#[inline(always)]
fn bsub<const BOUND: usize>(&self, val: u8) -> Self{
if *self >= val {
*self - val
} else {
(BOUND as u8-(val%BOUND as u8))+*self
}
}
}
impl DoesNotCoalesce for u8 {}
impl<T: DoesNotCoalesce> Coalesce for T {
fn coalesce(&mut self, _with: &Self) -> Result<(), QueueError> {
Err(QueueError::CannotCoalesce)
}
}
impl<E, const SIZE: usize> Default for RingQ<E,SIZE>
where
E: Clone + Coalesce + Copy
{
fn default() -> Self {
RingQ::<E,SIZE>::new()
}
}
impl<E, const SIZE: usize> RingQ<E,SIZE>
where
E: Clone + Coalesce + Copy
{
pub fn new() -> Self {
Self {
queue: [None; SIZE],
head: 0,
tail: 0
}
}
pub fn new_with(event: E) -> Self {
let mut new = Self::new();
panic_if_err!(new.append_nolock(event));
new
}
#[allow(dead_code)]
pub fn len(&self) -> u8 {
self.head.bsub::<SIZE>(self.tail)
}
pub fn consume_nolock(&mut self) -> Option<E> {
let tail = self.tail;
if self.head == tail {
None
} else {
let consumed = core::mem::replace(&mut self.queue[tail as usize], None);
self.tail.binc::<SIZE>();
consumed
}
}
pub fn append_nolock(&mut self, element: E) -> Result<(), QueueError> {
let head = self.head;
if head != self.tail { let prev = head.bsub::<SIZE>(1);
match &mut self.queue[prev as usize] {
None => {}
Some(queued_event) => {
if let Ok(()) = queued_event.coalesce(&element) {
return Ok(());
}
}
}
}
if head != (self.tail.bsub::<SIZE>(1)) { self.queue[head as usize] = Some(element);
self.head.binc::<SIZE>();
Ok(())
} else {
Err(QueueError::QueueFull)
}
}
pub fn append_blocking_nolock(&mut self, element: E) {
while self.append_nolock(element.clone()).is_err() {
}
}
}
#[cfg(test)]
mod tests {
use crate::private::ringq::{RingQ, DoesNotCoalesce, Coalesce, QueueError, BoundedMaths};
#[derive(Clone,Copy,PartialEq,Eq,Debug)]
struct TestEvent {
num: u8
}
impl DoesNotCoalesce for TestEvent {}
#[derive(Clone,Copy,PartialEq,Eq,Debug)]
struct CoalescingTestEvent {
num: u8
}
impl Coalesce for CoalescingTestEvent {
fn coalesce(&mut self, with: &Self) -> Result<(), QueueError> {
self.num += with.num;
Ok(())
}
}
#[test]
fn test_ringq() {
let mut queue : RingQ<TestEvent,10> = RingQ::new();
println!("Initial queue length: {}", queue.len());
assert_eq!(queue.len(), 0);
let test_ev1 = TestEvent { num: 0 };
queue.append_nolock(test_ev1);
println!("Queue length after adding 1 event: {}", queue.len());
assert_eq!(queue.len(), 1);
}
#[test]
#[should_panic]
fn test_ringq_bounds() {
let mut queue: RingQ<TestEvent,10> = RingQ::new();
for i in 1..=9 {
let test_ev1 = TestEvent { num: 0 };
queue.append_nolock(test_ev1);
println!("Appended element {}", i);
}
println!("Queue length: {}", queue.len());
println!("Succesfully added 9 events; next should panic");
let test_ev1 = TestEvent { num: 0 };
if let Ok(_) = queue.append_nolock(test_ev1) {
} else {
panic!();
}
}
#[test]
fn test_ringq_consume() {
let mut queue : RingQ<TestEvent,4> = RingQ::new();
println!("Initial queue length: {}", queue.len());
assert_eq!(queue.len(), 0);
let test_ev1 = TestEvent { num: 1 };
let test_ev2 = TestEvent { num: 2 };
let test_ev3 = TestEvent { num: 3 };
queue.append_nolock(test_ev1.clone());
queue.append_nolock(test_ev2.clone());
queue.append_nolock(test_ev3.clone());
println!("Queue length after adding 3 events: {}", queue.len());
assert_eq!(queue.len(), 3);
let consumed_ev1 = queue.consume_nolock().unwrap();
let consumed_ev2 = queue.consume_nolock().unwrap();
println!("Queue length after consuming 2 events: {}", queue.len());
assert_eq!(queue.len(), 1);
let test_ev4 = TestEvent { num: 4 };
let test_ev5 = TestEvent { num: 5 };
queue.append_nolock(test_ev4.clone());
queue.append_nolock(test_ev5.clone());
println!("Queue length after adding 2 more events: {}", queue.len());
assert_eq!(queue.len(), 3);
let consumed_ev3 = queue.consume_nolock().unwrap();
let consumed_ev4 = queue.consume_nolock().unwrap();
let consumed_ev5 = queue.consume_nolock().unwrap();
println!("Queue length after consuming 3 more events: {}", queue.len());
assert_eq!(queue.len(), 0);
let inserted = [ test_ev1, test_ev2, test_ev3, test_ev4, test_ev5 ];
let consumed = [ consumed_ev1, consumed_ev2, consumed_ev3, consumed_ev4, consumed_ev5 ];
assert_eq!(inserted, consumed);
}
#[test]
fn test_ringq_coalesce() {
let mut queue : RingQ<CoalescingTestEvent, 4> = RingQ::new();
let test_ev1 = CoalescingTestEvent { num: 1 };
let test_ev2 = CoalescingTestEvent { num: 2 };
let test_ev3 = CoalescingTestEvent { num: 3 };
let test_ev4 = CoalescingTestEvent { num: 4 };
let test_ev5 = CoalescingTestEvent { num: 5 };
println!("Initial queue length: {}", queue.len());
assert_eq!(queue.len(), 0);
queue.append_nolock(test_ev1.clone());
queue.append_nolock(test_ev2.clone());
queue.append_nolock(test_ev3.clone());
queue.append_nolock(test_ev4.clone());
queue.append_nolock(test_ev5.clone());
println!("Queue length after adding 5 coalescable events: {}", queue.len());
assert_eq!(queue.len(), 1);
let consumed_ev1 = queue.consume_nolock().unwrap();
println!("Consumed event: {:?}", &consumed_ev1);
assert_eq!(consumed_ev1.num, 15);
}
#[test]
fn test_bounded_maths() {
let mut val:u8 = 0;
assert_eq!(val.bsub::<10>(1u8), 9);
assert_eq!(val, 0);
val.binc::<10>();
assert_eq!(val, 1);
val.binc::<10>();
assert_eq!(val, 2);
val.bdec::<10>();
assert_eq!(val, 1);
val.bdec::<10>();
assert_eq!(val, 0);
val.bdec::<10>();
assert_eq!(val, 9);
val.binc::<10>();
assert_eq!(val, 0);
let zero:u8 = 0;
let one:u8 = 1;
let five:u8 = 5;
let four:u8 = 4;
assert_eq!(five.bsub::<10>(four), 1);
assert_eq!(four.bsub::<10>(five), 9);
assert_eq!(zero.bsub::<10>(one), 9);
assert_eq!(one.bsub::<10>(zero), 1);
}
}