use core::cmp::min;
use crate::utils::init::{init, Init};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RingBuf<const N: usize> {
buf: crate::utils::storage::Vec<u8, N>,
start: usize,
end: usize,
non_empty: bool,
}
impl<const N: usize> Default for RingBuf<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> RingBuf<N> {
#[inline(always)]
pub const fn new() -> Self {
Self {
buf: crate::utils::storage::Vec::new(),
start: 0,
end: 0,
non_empty: false,
}
}
pub fn init() -> impl Init<Self> {
init!(Self {
buf <- crate::utils::storage::Vec::init(),
start: 0,
end: 0,
non_empty: false,
})
}
#[inline(always)]
pub fn push(&mut self, data: &[u8]) -> usize {
unwrap!(self.buf.resize_default(N));
let mut offset = 0;
while offset < data.len() {
let len = min(self.buf.len() - self.end, data.len() - offset);
self.buf[self.end..self.end + len].copy_from_slice(&data[offset..offset + len]);
offset += len;
if self.non_empty && self.start >= self.end && self.start < self.end + len {
self.start = self.end + len;
}
self.end += len;
self.wrap();
self.non_empty = true;
}
self.len()
}
#[inline(always)]
pub fn push_byte(&mut self, data: u8) -> usize {
unwrap!(self.buf.resize_default(N));
self.buf[self.end] = data;
if self.non_empty && self.start == self.end {
self.start = self.end + 1;
}
self.end += 1;
self.wrap();
self.non_empty = true;
self.len()
}
#[inline(always)]
pub fn pop_byte(&mut self) -> Option<u8> {
let mut buf = [0; 1];
if self.pop(&mut buf) == 1 {
Some(buf[0])
} else {
None
}
}
#[inline(always)]
pub fn pop(&mut self, out_buf: &mut [u8]) -> usize {
let mut offset = 0;
while offset < out_buf.len() && self.non_empty {
let len = min(
if self.start < self.end {
self.end
} else {
self.buf.len()
} - self.start,
out_buf.len() - offset,
);
out_buf[offset..offset + len].copy_from_slice(&self.buf[self.start..self.start + len]);
self.start += len;
self.wrap();
if self.start == self.end {
self.non_empty = false
}
offset += len;
}
offset
}
#[inline(always)]
pub fn is_full(&self) -> bool {
self.start == self.end && self.non_empty
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
!self.non_empty
}
#[inline(always)]
#[allow(unused)]
pub fn len(&self) -> usize {
if !self.non_empty {
0
} else if self.start < self.end {
self.end - self.start
} else {
self.buf.len() + self.end - self.start
}
}
#[inline(always)]
#[allow(unused)]
pub fn free(&self) -> usize {
N - self.len()
}
#[inline(always)]
pub fn clear(&mut self) {
self.start = 0;
self.end = 0;
self.non_empty = false;
}
#[inline(always)]
fn wrap(&mut self) {
if self.start == self.buf.len() {
self.start = 0;
}
if self.end == self.buf.len() {
self.end = 0;
}
}
}
impl<const N: usize> Iterator for RingBuf<N> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
self.pop_byte()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_pop() {
let mut rb = RingBuf::<4>::new();
assert!(rb.is_empty());
rb.push(&[0, 1, 2]);
assert_eq!(3, rb.len());
assert!(!rb.is_empty());
assert!(!rb.is_full());
rb.push(&[3]);
assert_eq!(4, rb.len());
assert!(!rb.is_empty());
assert!(rb.is_full());
let mut buf = [0; 256];
let len = rb.pop(&mut buf);
assert_eq!(4, len);
assert_eq!(&buf[0..4], &[0, 1, 2, 3]);
assert!(rb.is_empty());
rb.push(&[0, 1, 2, 3, 4, 5]);
assert_eq!(4, rb.len());
assert!(!rb.is_empty());
assert!(rb.is_full());
let len = rb.pop(&mut buf[..3]);
assert_eq!(3, len);
assert_eq!(&buf[0..len], &[2, 3, 4]);
assert!(!rb.is_empty());
assert!(!rb.is_full());
let len = rb.pop(&mut buf);
assert_eq!(1, len);
assert_eq!(&buf[0..len], &[5]);
assert!(rb.is_empty());
assert!(!rb.is_full());
let len = rb.pop(&mut buf);
assert_eq!(0, len);
assert!(rb.is_empty());
assert!(!rb.is_full());
}
}