use super::*;
pub struct DmaCircularBufferRx<T: Sized, CH> {
ch: CH,
buf: CircularBuffer<T>,
}
impl<T, CH> DmaCircularBufferRx<T, CH>
where
T: Sized + Copy,
CH: DmaChannel,
{
pub fn new(mut ch: CH, peripheral_addr: usize, buf_size: usize) -> Self {
let buf = CircularBuffer::<T>::new(buf_size);
ch.stop();
ch.set_memory_buf_for_peripheral(buf.as_slice());
ch.set_peripheral_address::<T>(peripheral_addr, false, false, true);
ch.start();
Self { ch, buf }
}
#[inline]
pub fn pop_slice(&mut self, max: usize) -> Option<&[T]> {
self.buf.pop_slice(self.ch.get_unprocessed_len(), max)
}
}
pub struct CircularBuffer<T> {
recv_buf: Vec<T>,
read_idx: usize,
}
impl<T: Sized + Copy> CircularBuffer<T> {
fn new(buf_size: usize) -> Self {
let mut recv_buf = Vec::<T>::with_capacity(buf_size);
#[allow(clippy::uninit_vec)]
unsafe {
recv_buf.set_len(buf_size)
}
Self {
recv_buf,
read_idx: 0,
}
}
fn pop_slice(&mut self, unprocessed_len: usize, max: usize) -> Option<&[T]> {
let dma_recv_idx = if unprocessed_len == 0 {
0
} else {
self.recv_buf.len() - unprocessed_len
};
if self.read_idx == dma_recv_idx {
return None;
}
let ret;
if dma_recv_idx < self.read_idx {
if max > self.recv_buf.len() - self.read_idx {
ret = Some(&self.recv_buf[self.read_idx..]);
self.read_idx = 0;
} else {
let end = self.read_idx + max;
ret = Some(&self.recv_buf[self.read_idx..end]);
self.read_idx = end;
}
} else if max > dma_recv_idx - self.read_idx {
ret = Some(&self.recv_buf[self.read_idx..dma_recv_idx]);
self.read_idx = dma_recv_idx;
} else {
let end = self.read_idx + max;
ret = Some(&self.recv_buf[self.read_idx..end]);
self.read_idx = end;
}
ret
}
fn as_slice(&self) -> &[T] {
self.recv_buf.as_slice()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn circular_buffer() {
let buf_size = 13;
let mut buf = CircularBuffer::new(buf_size);
assert_eq!(buf.recv_buf.len(), buf_size);
for i in 0..buf_size {
buf.recv_buf[i] = i as u8;
}
assert_eq!(
buf.pop_slice(5, usize::MAX),
Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
);
assert_eq!(buf.pop_slice(5, usize::MAX), None);
assert_eq!(
buf.pop_slice(0, usize::MAX),
Some([8u8, 9, 10, 11, 12].as_slice())
);
assert_eq!(buf.pop_slice(0, usize::MAX), None);
assert_eq!(buf.pop_slice(buf_size, usize::MAX), None);
assert_eq!(buf.pop_slice(5, 5), Some([0u8, 1, 2, 3, 4].as_slice()));
assert_eq!(buf.pop_slice(5, 5), Some([5u8, 6, 7].as_slice()));
assert_eq!(buf.pop_slice(5, 5), None);
assert_eq!(
buf.pop_slice(0, usize::MAX),
Some([8u8, 9, 10, 11, 12].as_slice())
);
assert_eq!(
buf.pop_slice(5, usize::MAX),
Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
);
assert_eq!(
buf.pop_slice(10, usize::MAX),
Some([8u8, 9, 10, 11, 12].as_slice())
);
assert_eq!(buf.pop_slice(10, usize::MAX), Some([0u8, 1, 2].as_slice()));
assert_eq!(buf.pop_slice(10, usize::MAX), None);
}
}