Currently using stack-allocated buffers is impossible without unsafe. Here are two ideas on how to do this, coming from our conversations in Matrix :)
I'm not sure which one is best, we should explore tradeoffs and decide (or have both?)
Method 1: closure-based
pub struct Buffer<'a> {
inner: &'a mut [u8],
}
unsafe impl<'a> ReadBuffer for Buffer<'a> {
type Word = u8;
unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
(self.inner.as_ptr(), self.inner.len())
}
}
unsafe impl<'a> WriteBuffer for Buffer<'a> {
type Word = u8;
unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
(self.inner.as_mut_ptr(), self.inner.len())
}
}
fn with_buffer<'a, R>(buf: &'a mut [u8], f: impl FnOnce(Buffer<'a>) -> (R, Buffer<'a>)) -> R {
let orig_loc = (buf.as_ptr(), buf.len());
let (r, ret_buf) = f(Buffer { inner: buf });
let ret_loc = (ret_buf.inner.as_ptr(), ret_buf.inner.len());
core::assert_eq!(orig_loc, ret_loc); // Not sure if this is needed for safety.
r
}
Method 2: flag-based
#[feature(min_const_generics)]
use core::marker::PhantomPinned;
use core::panic;
use core::pin::Pin;
//use embedded_dma::{ReadBuffer, WriteBuffer};
use futures::pin_mut;
struct DmaBuffer<B: ?Sized> {
pinned: PhantomPinned,
in_use: bool,
buf: B,
}
impl<B> DmaBuffer<B> {
pub fn new(b: B) -> Self {
Self {
pinned: PhantomPinned,
in_use: false,
buf: b,
}
}
}
impl<const N: usize> DmaBuffer<[u8; N]> {
pub fn borrow<'a>(self: Pin<&'a mut Self>) -> DmaBufferBorrow<'a, [u8]> {
let this = unsafe { self.get_unchecked_mut() };
if this.in_use {
panic!("You can't borrow a buffer two times!")
}
this.in_use = true;
DmaBufferBorrow(unsafe { Pin::new_unchecked(this) })
}
}
impl<B: ?Sized> Drop for DmaBuffer<B> {
fn drop(&mut self) {
if self.in_use {
panic!("You leaked a buffer borrow!")
}
}
}
struct DmaBufferBorrow<'a, B: ?Sized>(Pin<&'a mut DmaBuffer<B>>);
impl<'a, B: ?Sized> Drop for DmaBufferBorrow<'a, B> {
fn drop(&mut self) {
unsafe { self.0.as_mut().get_unchecked_mut() }.in_use = false;
}
}
fn use_buffer<'a>(buf: DmaBufferBorrow<'a, [u8]>) {}
unsafe impl<'a> ReadBuffer for DmaBufferBorrow<'a, [u8]> {
type Word = u8;
unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
(self.0.buf.as_ptr(), self.0.buf.len())
}
}
unsafe impl<'a> WriteBuffer for DmaBufferBorrow<'a, [u8]> {
type Word = u8;
unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
let buf = unsafe { self.0.as_mut().get_unchecked_mut() };
(buf.buf.as_mut_ptr(), buf.buf.len())
}
}
fn main() {
let mut buf = DmaBuffer::new([0u8; 1024]);
pin_mut!(buf);
println!("in use: {}", buf.in_use);
let mut borrow = buf.as_mut().borrow();
//println!("in use: {}", buf.in_use); This would print true
use_buffer(borrow);
println!("in use: {}", buf.in_use);
}
Currently using stack-allocated buffers is impossible without unsafe. Here are two ideas on how to do this, coming from our conversations in Matrix :)
I'm not sure which one is best, we should explore tradeoffs and decide (or have both?)
Method 1: closure-based
Method 2: flag-based