#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![feature(maybe_uninit_uninit_array)]
#![feature(const_maybe_uninit_uninit_array)]
#![feature(const_maybe_uninit_write)]
#![no_std]
use core::fmt::{self, Debug};
use core::mem::MaybeUninit;
use const_assert::{Assert, IsTrue};
use likeness::unlikely;
pub struct Circular<const N: usize, T>
where
T: Unpin,
{
target_buffer: [MaybeUninit<T>; N],
target_head: usize,
target_tail: usize,
}
impl<const N: usize, T> Circular<N, T>
where
T: Unpin,
Assert<{ N != 0 }>: IsTrue,
{
#[inline]
pub const fn new() -> Self {
let target_buffer = MaybeUninit::uninit_array();
let target_head = usize::MIN;
let target_tail = target_head;
Self {
target_buffer,
target_head,
target_tail,
}
}
#[inline]
pub fn push(&mut self, target_value: T) {
let Self {
target_buffer,
target_head,
target_tail,
} = self;
unsafe {
target_buffer
.get_unchecked_mut(*target_head)
.write(target_value);
}
*target_head = target_head.saturating_add(1);
*target_head %= N;
if unlikely(*target_head == *target_tail) {
*target_tail = target_tail.saturating_add(1);
*target_tail %= N;
}
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
let Self {
target_buffer,
target_head,
target_tail,
} = self;
if unlikely(*target_head == *target_tail) {
return None;
}
let target_value = unsafe {
Some(
target_buffer
.get_unchecked_mut(*target_tail)
.assume_init_read(),
)
};
*target_tail = target_tail.saturating_add(1);
*target_tail %= N;
target_value
}
#[inline]
pub const fn is_empty(&self) -> bool {
let Self {
target_head,
target_tail,
..
} = self;
*target_head == *target_tail
}
}
impl<const N: usize, T: Clone> Clone for Circular<N, T>
where
T: Unpin + Clone,
MaybeUninit<T>: Clone,
{
fn clone(&self) -> Self {
Self {
target_buffer: self.target_buffer.clone(),
target_head: self.target_head.clone(),
target_tail: self.target_tail.clone(),
}
}
}
impl<const N: usize, T: Debug> Debug for Circular<N, T>
where
T: Unpin,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Circular")
.field("target_buffer", &self.target_buffer)
.field("target_head", &self.target_head)
.field("target_tail", &self.target_tail)
.finish()
}
}
#[cfg(test)]
mod tests {
use crate::Circular;
#[test]
fn push_and_pop() {
let mut last_scores: Circular<16, usize> = Circular::new();
{
last_scores.push(1);
assert!(matches!(last_scores.pop(), Some(1)));
}
{
assert!(matches!(last_scores.pop(), None));
}
}
#[test]
fn wrapping_behaviour() {
const DISCRIMINANT_VALUE: usize = 0x10;
const COMMONFOUND_VALUE: usize = 0x20;
const ARRAY_SIZE: usize = 0x10;
let mut last_scores: Circular<ARRAY_SIZE, usize> = Circular::new();
{
for _ in 0..ARRAY_SIZE {
last_scores.push(COMMONFOUND_VALUE);
}
while let Some(target_value) = last_scores.pop() {
assert_ne!(target_value, DISCRIMINANT_VALUE)
}
}
{
assert!(matches!(last_scores.pop(), None));
}
}
}