use core::{
ops::{Deref, DerefMut},
ptr::{addr_of, addr_of_mut, NonNull},
sync::atomic::{AtomicBool, AtomicU8, Ordering},
unreachable,
};
use grounded::{const_init::ConstInit, uninit::GroundedArrayCell};
use crate::CmdAddr;
pub struct FrameStorage<const N: usize> {
frames: GroundedArrayCell<RawFrame, N>,
once: AtomicBool,
}
impl<const N: usize> FrameStorage<N> {
pub const fn new() -> Self {
Self {
frames: GroundedArrayCell::const_init(),
once: AtomicBool::new(false),
}
}
pub fn take(&'static self) -> Option<RawFrameSlice> {
self.take_gac()
.map(|s| unsafe { RawFrameSlice::from_static(s) })
}
fn take_gac(&'static self) -> Option<&'static GroundedArrayCell<RawFrame, N>> {
critical_section::with(|_| {
let old = self.once.load(Ordering::Acquire);
self.once.store(true, Ordering::Release);
!old
})
.then_some(&self.frames)
}
}
#[repr(C)]
pub(crate) struct RawFrame {
data: [u8; 255],
freelen: AtomicU8,
}
pub struct FrameBox {
ptr: NonNull<RawFrame>,
}
unsafe impl Send for FrameBox {}
impl FrameBox {
unsafe fn freelen_ref(&self) -> &AtomicU8 {
let fl_ptr = addr_of!((*self.ptr.as_ptr()).freelen);
&*fl_ptr
}
pub fn set_len(&mut self, len: usize) {
if len == 0 || len > 255 {
unreachable!()
}
unsafe {
let fl = self.freelen_ref();
fl.store(len as u8, Ordering::Relaxed);
}
}
}
impl Deref for FrameBox {
type Target = [u8];
fn deref(&self) -> &Self::Target {
let len = unsafe { self.freelen_ref().load(Ordering::Relaxed) };
assert!(len != 0);
let data_ptr: *const u8 = unsafe {
let arr_ptr: *const [u8; 255] = addr_of!((*self.ptr.as_ptr()).data);
arr_ptr.cast()
};
unsafe { core::slice::from_raw_parts(data_ptr, len as usize) }
}
}
impl DerefMut for FrameBox {
fn deref_mut(&mut self) -> &mut Self::Target {
let len = unsafe { self.freelen_ref().load(Ordering::Relaxed) };
assert!(len != 0);
let data_ptr: *mut u8 = unsafe {
let arr_ptr: *mut [u8; 255] = addr_of_mut!((*self.ptr.as_ptr()).data);
arr_ptr.cast()
};
unsafe { core::slice::from_raw_parts_mut(data_ptr, len as usize) }
}
}
impl Drop for FrameBox {
fn drop(&mut self) {
let ptr: *mut RawFrame = self.ptr.as_ptr();
unsafe {
let atom_ptr: *mut AtomicU8 = addr_of_mut!((*ptr).freelen);
let atom: &AtomicU8 = &*atom_ptr;
atom.store(RawFrame::FREE, Ordering::Release);
}
}
}
impl RawFrame {
const FREE: u8 = 0;
const MAX_LEN: u8 = 255;
}
impl ConstInit for RawFrame {
#[allow(clippy::declare_interior_mutable_const)]
const VAL: Self = RawFrame {
data: [0u8; Self::MAX_LEN as usize],
freelen: AtomicU8::new(0),
};
}
unsafe impl Send for RawFrameSlice {}
pub struct RawFrameSlice {
start: NonNull<RawFrame>,
len: usize,
next_idx: usize,
}
impl RawFrameSlice {
pub(crate) unsafe fn from_static<const N: usize>(
buf: &'static GroundedArrayCell<RawFrame, N>,
) -> Self {
Self {
start: NonNull::new_unchecked(buf.as_mut_ptr()),
len: N,
next_idx: 0,
}
}
pub const fn uninit() -> Self {
Self {
start: NonNull::dangling(),
len: 0,
next_idx: 0,
}
}
pub fn count_allocatable(&self) -> usize {
if self.len == 0 {
return 0;
}
let mut ct = 0;
let start_ptr: *mut RawFrame = self.start.as_ptr();
for idx in 0..self.len {
let ptr: *mut RawFrame = unsafe { start_ptr.add(idx) };
{
let atom_ptr: *const AtomicU8 = unsafe { addr_of!((*ptr).freelen) };
let atom: &AtomicU8 = unsafe { &*atom_ptr };
if atom.load(Ordering::Acquire) == RawFrame::FREE {
ct += 1;
}
}
}
ct
}
pub fn allocate_raw(&mut self) -> Option<FrameBox> {
if self.len == 0 {
return None;
}
if self.next_idx >= self.len {
self.next_idx = 0;
}
let start_ptr: *mut RawFrame = self.start.as_ptr();
let idxes = (self.next_idx..self.len).chain(0..self.next_idx);
for idx in idxes {
let ptr: *mut RawFrame = unsafe { start_ptr.add(idx) };
{
let atom_ptr: *mut AtomicU8 = unsafe { addr_of_mut!((*ptr).freelen) };
let atom: &AtomicU8 = unsafe { &*atom_ptr };
if atom.load(Ordering::Acquire) == RawFrame::FREE {
atom.store(RawFrame::MAX_LEN, Ordering::Release);
self.next_idx = idx + 1;
} else {
continue;
}
}
return Some(FrameBox {
ptr: NonNull::new(ptr)?,
});
}
None
}
pub fn split(&mut self, at: usize) -> Option<Self> {
if (at == 0) || (at > self.len) {
return None;
}
let len_new = self.len - at;
self.len = at;
Some(RawFrameSlice {
start: unsafe { NonNull::new_unchecked(self.start.as_ptr().add(at)) },
len: len_new,
next_idx: 0,
})
}
pub fn capacity(&self) -> usize {
self.len
}
}
pub struct WireFrameBox {
fb: FrameBox,
}
impl WireFrameBox {
pub(crate) fn new_unchecked(fb: FrameBox) -> Self {
Self { fb }
}
#[inline]
pub fn cmd_addr(&self) -> CmdAddr {
self.fb[0].try_into().unwrap()
}
#[inline]
pub fn payload(&self) -> &[u8] {
&self.fb[1..]
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
&mut self.fb[1..]
}
#[inline]
pub fn into_inner(self) -> FrameBox {
self.fb
}
#[inline]
pub fn is_empty(&self) -> bool {
self.fb.len() == 1
}
#[inline]
pub fn len(&self) -> usize {
self.fb.len() - 1
}
pub fn set_len(&mut self, len: usize) {
self.fb.set_len(1 + len)
}
}
pub struct SendFrameBox {
fb: FrameBox,
}
impl From<FrameBox> for SendFrameBox {
fn from(value: FrameBox) -> Self {
Self { fb: value }
}
}
impl SendFrameBox {
#[inline]
pub fn payload(&self) -> &[u8] {
&self.fb[1..]
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
&mut self.fb[1..]
}
#[inline]
pub fn into_inner(self) -> FrameBox {
self.fb
}
#[inline]
pub fn is_empty(&self) -> bool {
self.fb.len() == 1
}
#[inline]
pub fn len(&self) -> usize {
self.fb.len() - 1
}
pub fn set_len(&mut self, len: usize) {
self.fb.set_len(1 + len)
}
}