lowlet 0.1.2

Low-latency IPC library using shared memory and lock-free structures
Documentation
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()
    }
}