use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use core::sync::atomic::{AtomicBool, Ordering};
pub struct BufferPool<const SLOTS: usize, const LEN: usize> {
store: UnsafeCell<[[u8; LEN]; SLOTS]>,
claimed: [AtomicBool; SLOTS],
}
unsafe impl<const SLOTS: usize, const LEN: usize> Sync for BufferPool<SLOTS, LEN> {}
impl<const SLOTS: usize, const LEN: usize> BufferPool<SLOTS, LEN> {
#[must_use]
pub const fn new() -> Self {
const {
assert!(
LEN >= 16,
"BufferPool slot must hold at least a 16-byte SOME/IP header"
);
};
Self {
store: UnsafeCell::new([[0u8; LEN]; SLOTS]),
claimed: [const { AtomicBool::new(false) }; SLOTS],
}
}
pub fn claim(&'static self) -> Option<BufferLease> {
let (buf, flag) = self.try_claim_slot()?;
Some(BufferLease {
buf,
len: LEN,
flag,
#[cfg(feature = "_alloc")]
_owner: None,
})
}
fn try_claim_slot(&self) -> Option<(NonNull<u8>, NonNull<AtomicBool>)> {
for (idx, flag) in self.claimed.iter().enumerate() {
if flag
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
let slot_ptr = unsafe { self.store.get().cast::<u8>().add(idx * LEN) };
unsafe { core::ptr::write_bytes(slot_ptr, 0, LEN) };
let buf = unsafe { NonNull::new_unchecked(slot_ptr) };
let flag = NonNull::from(flag);
return Some((buf, flag));
}
}
None
}
}
#[cfg(feature = "_alloc")]
impl<const SLOTS: usize, const LEN: usize> BufferPool<SLOTS, LEN> {
pub fn claim_arc(self: &alloc::sync::Arc<Self>) -> Option<BufferLease> {
let (buf, flag) = self.try_claim_slot()?;
Some(BufferLease {
buf,
len: LEN,
flag,
_owner: Some(self.clone()),
})
}
}
impl<const SLOTS: usize, const LEN: usize> Default for BufferPool<SLOTS, LEN> {
fn default() -> Self {
Self::new()
}
}
impl<const SLOTS: usize, const LEN: usize> core::fmt::Debug for BufferPool<SLOTS, LEN> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BufferPool")
.field("slots", &SLOTS)
.field("len", &LEN)
.finish_non_exhaustive()
}
}
pub struct BufferLease {
buf: NonNull<u8>,
len: usize,
flag: NonNull<AtomicBool>,
#[cfg(feature = "_alloc")]
_owner: Option<alloc::sync::Arc<dyn core::any::Any + Send + Sync>>,
}
unsafe impl Send for BufferLease {}
impl Deref for BufferLease {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len) }
}
}
impl DerefMut for BufferLease {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.buf.as_ptr(), self.len) }
}
}
impl Drop for BufferLease {
fn drop(&mut self) {
unsafe { self.flag.as_ref() }.store(false, Ordering::Release);
}
}