use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::err::{Error, Result};
const CACHE_LINE: usize = 64;
#[repr(C, align(64))]
pub struct SpscQueue<T, const N: usize> {
head: AtomicUsize,
_pad1: [u8; CACHE_LINE - 8],
tail: AtomicUsize,
_pad2: [u8; CACHE_LINE - 8],
buffer: [UnsafeCell<MaybeUninit<T>>; N],
}
unsafe impl<T: Send, const N: usize> Send for SpscQueue<T, N> {}
unsafe impl<T: Send, const N: usize> Sync for SpscQueue<T, N> {}
impl<T, const N: usize> SpscQueue<T, N> {
const MASK: usize = N - 1;
pub fn new() -> Self {
assert!(N.is_power_of_two(), "capacity must be power of two");
Self {
head: AtomicUsize::new(0),
_pad1: [0; CACHE_LINE - 8],
tail: AtomicUsize::new(0),
_pad2: [0; CACHE_LINE - 8],
buffer: unsafe { MaybeUninit::uninit().assume_init() },
}
}
#[inline]
pub fn push(&self, value: T) -> Result<()> {
let tail = self.tail.load(Ordering::Relaxed);
let next = tail.wrapping_add(1);
if next.wrapping_sub(self.head.load(Ordering::Acquire)) > N {
return Err(Error::QueueFull);
}
unsafe {
(*self.buffer[tail & Self::MASK].get()).write(value);
}
self.tail.store(next, Ordering::Release);
Ok(())
}
#[inline]
pub fn pop(&self) -> Result<T> {
let head = self.head.load(Ordering::Relaxed);
if head == self.tail.load(Ordering::Acquire) {
return Err(Error::QueueEmpty);
}
let value = unsafe { (*self.buffer[head & Self::MASK].get()).assume_init_read() };
self.head.store(head.wrapping_add(1), Ordering::Release);
Ok(value)
}
pub fn peek(&self) -> Option<&T> {
let head = self.head.load(Ordering::Relaxed);
if head == self.tail.load(Ordering::Acquire) {
return None;
}
Some(unsafe { (*self.buffer[head & Self::MASK].get()).assume_init_ref() })
}
#[inline]
pub fn is_empty(&self) -> bool {
self.head.load(Ordering::Acquire) == self.tail.load(Ordering::Acquire)
}
#[inline]
pub fn len(&self) -> usize {
let tail = self.tail.load(Ordering::Acquire);
let head = self.head.load(Ordering::Acquire);
tail.wrapping_sub(head)
}
#[inline]
pub fn capacity(&self) -> usize {
N
}
}
impl<T, const N: usize> Default for SpscQueue<T, N> {
fn default() -> Self {
Self::new()
}
}