diff options
Diffstat (limited to 'embassy-sync/src/ring_buffer.rs')
-rw-r--r-- | embassy-sync/src/ring_buffer.rs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/embassy-sync/src/ring_buffer.rs b/embassy-sync/src/ring_buffer.rs new file mode 100644 index 00000000..52108402 --- /dev/null +++ b/embassy-sync/src/ring_buffer.rs @@ -0,0 +1,146 @@ +pub struct RingBuffer<const N: usize> { + buf: [u8; N], + start: usize, + end: usize, + empty: bool, +} + +impl<const N: usize> RingBuffer<N> { + pub const fn new() -> Self { + Self { + buf: [0; N], + start: 0, + end: 0, + empty: true, + } + } + + pub fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + self.buf.len() - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + pub fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = self.wrap(self.end + n); + self.empty = false; + } + + pub fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + self.buf.len() - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + pub fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = self.wrap(self.start + n); + self.empty = self.start == self.end; + } + + pub fn is_full(&self) -> bool { + self.start == self.end && !self.empty + } + + pub fn is_empty(&self) -> bool { + self.empty + } + + #[allow(unused)] + pub fn len(&self) -> usize { + if self.empty { + 0 + } else if self.start < self.end { + self.end - self.start + } else { + N + self.end - self.start + } + } + + pub fn clear(&mut self) { + self.start = 0; + self.end = 0; + self.empty = true; + } + + fn wrap(&self, n: usize) -> usize { + assert!(n <= self.buf.len()); + if n == self.buf.len() { + 0 + } else { + n + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn push_pop() { + let mut rb: RingBuffer<4> = RingBuffer::new(); + let buf = rb.push_buf(); + assert_eq!(4, buf.len()); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + buf[3] = 4; + rb.push(4); + + let buf = rb.pop_buf(); + assert_eq!(4, buf.len()); + assert_eq!(1, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(3, buf.len()); + assert_eq!(2, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(2, buf.len()); + assert_eq!(3, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(1, buf.len()); + assert_eq!(4, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(0, buf.len()); + + let buf = rb.push_buf(); + assert_eq!(4, buf.len()); + } +} |