use core::{
cell::UnsafeCell,
sync::atomic::{AtomicBool, Ordering},
};
pub struct EndpointMemory<const SIZE: usize> {
buffer: UnsafeCell<[u8; SIZE]>,
taken: AtomicBool,
}
impl<const SIZE: usize> Default for EndpointMemory<SIZE> {
fn default() -> Self {
Self::new()
}
}
impl<const SIZE: usize> EndpointMemory<SIZE> {
pub const fn new() -> Self {
Self {
buffer: UnsafeCell::new([0; SIZE]),
taken: AtomicBool::new(false),
}
}
pub(crate) fn allocator(&'static self) -> Option<Allocator> {
if self.taken.swap(true, Ordering::SeqCst) {
None
} else {
Some(Allocator::new(unsafe { &mut *self.buffer.get() }))
}
}
}
unsafe impl<const SIZE: usize> Sync for EndpointMemory<SIZE> {}
pub struct Allocator {
start: *mut u8,
ptr: *mut u8,
}
unsafe impl Send for crate::buffer::Allocator {}
impl Allocator {
fn new(buffer: &'static mut [u8]) -> Self {
unsafe { Self::from_buffer(buffer) }
}
pub(crate) unsafe fn from_buffer(buffer: &mut [u8]) -> Self {
let start = buffer.as_mut_ptr();
let ptr = unsafe { start.add(buffer.len()) };
Allocator { start, ptr }
}
pub fn allocate(&mut self, size: usize) -> Option<Buffer> {
let ptr = self.ptr as usize;
let ptr = ptr.checked_sub(size)?;
let start = self.start as usize;
if ptr < start {
None
} else {
self.ptr = ptr as *mut u8;
Some(Buffer {
ptr: self.ptr,
len: size,
})
}
}
}
pub struct Buffer {
ptr: *mut u8,
len: usize,
}
unsafe impl Send for Buffer {}
impl Buffer {
pub fn volatile_read(&self, buffer: &mut [u8]) -> usize {
let size = buffer.len().min(self.len);
buffer
.iter_mut()
.take(size)
.fold(self.ptr, |src, dst| unsafe {
*dst = src.read_volatile();
src.add(1)
});
size
}
pub fn volatile_write(&mut self, buffer: &[u8]) -> usize {
let size = buffer.len().min(self.len);
buffer.iter().take(size).fold(self.ptr, |dst, src| unsafe {
dst.write_volatile(*src);
dst.add(1)
});
size
}
pub fn as_ptr_mut(&mut self) -> *mut u8 {
self.ptr
}
pub fn len(&self) -> usize {
self.len
}
pub fn clean_invalidate_dcache(&self, len: usize) {
crate::cache::clean_invalidate_dcache_by_address(self.ptr as usize, self.len.min(len));
}
}
#[cfg(test)]
mod test {
use super::Allocator;
#[test]
fn allocate_entire_buffer() {
let mut buffer: [u8; 32] = [0; 32];
let mut alloc = unsafe { Allocator::from_buffer(&mut buffer) };
let ptr = alloc.allocate(32);
assert!(ptr.is_some());
assert_eq!(ptr.unwrap().ptr, buffer.as_mut_ptr());
let ptr = alloc.allocate(1);
assert!(ptr.is_none());
}
#[test]
fn allocate_partial_buffers() {
let mut buffer: [u8; 32] = [0; 32];
let mut alloc = unsafe { Allocator::from_buffer(&mut buffer) };
let ptr = alloc.allocate(7);
assert!(ptr.is_some());
assert_eq!(ptr.unwrap().ptr, unsafe { buffer.as_mut_ptr().add(32 - 7) });
let ptr = alloc.allocate(7);
assert!(ptr.is_some());
assert_eq!(ptr.unwrap().ptr, unsafe {
buffer.as_mut_ptr().add(32 - 14)
});
let ptr = alloc.allocate(19);
assert!(ptr.is_none());
}
#[test]
fn allocate_empty() {
let mut alloc = Allocator {
start: core::ptr::null_mut(),
ptr: core::ptr::null_mut(),
};
assert!(alloc.allocate(1).is_none());
}
}