use std::fmt;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RollingCharBufferError {
BufferFullError,
BufferEmptyError,
}
impl std::error::Error for RollingCharBufferError {}
impl fmt::Display for RollingCharBufferError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RollingCharBufferError::BufferFullError => write!(f, "Buffer is full"),
RollingCharBufferError::BufferEmptyError => write!(f, "Buffer is empty"),
}
}
}
#[derive(Debug)]
pub struct RollingCharBuffer<const CAP: usize> {
pub(crate) full: bool,
pub(crate) cap: usize,
start: usize,
end: usize,
buffer: Box<[char; CAP]>,
}
impl<const CAP: usize> Default for RollingCharBuffer<CAP> {
fn default() -> Self {
Self::new()
}
}
impl<const CAP: usize> RollingCharBuffer<CAP> {
pub fn new() -> Self {
RollingCharBuffer {
start: CAP - 1,
end: CAP - 1,
full: false,
cap: CAP,
buffer: Box::new(['x'; CAP]),
}
}
pub fn len(&self) -> usize {
if self.full {
self.cap
} else if self.end >= self.start {
self.end - self.start
} else {
self.cap - (self.start - self.end)
}
}
pub fn is_empty(&self) -> bool {
self.start == self.end && !self.full
}
pub fn is_full(&self) -> bool {
self.full
}
pub fn clear(&mut self) {
self.full = false;
self.start = self.end;
}
pub fn push(&mut self, c: char) -> Result<(), RollingCharBufferError> {
if self.full {
return Err(RollingCharBufferError::BufferFullError);
}
self.buffer[self.end] = c;
self.end = (self.end + 1) % self.cap;
if self.end == self.start {
self.full = true;
}
Ok(())
}
pub fn read(&mut self) -> Result<char, RollingCharBufferError> {
if self.is_empty() {
return Err(RollingCharBufferError::BufferEmptyError);
}
let c = self.buffer[self.start];
self.start = (self.start + 1) % self.cap;
if self.full {
self.full = false;
}
Ok(c)
}
pub fn pop(&mut self) -> Result<char, RollingCharBufferError> {
if self.is_empty() {
return Err(RollingCharBufferError::BufferEmptyError);
}
self.end = if self.end == 0 {
self.cap - 1
} else {
self.end - 1
};
if self.full {
self.full = false;
}
Ok(self.buffer[self.end])
}
pub fn prefix(&mut self, c: char) -> Result<(), RollingCharBufferError> {
if self.full {
return Err(RollingCharBufferError::BufferFullError);
}
self.start = if self.start == 0 {
self.cap - 1
} else {
self.start - 1
};
self.buffer[self.start] = c;
if self.end == self.start {
self.full = true;
}
Ok(())
}
pub fn extend(&mut self, charvec: &[char]) -> Result<usize, RollingCharBufferError> {
let free = self.cap - self.len();
if self.full || charvec.len() > free {
return Err(RollingCharBufferError::BufferFullError);
}
for &c in charvec {
self.push(c)?;
}
Ok(self.cap - self.len())
}
pub fn prepend(&mut self, cs: &[char]) -> Result<usize, RollingCharBufferError> {
let free = self.cap - self.len();
if self.full || cs.len() > free {
return Err(RollingCharBufferError::BufferFullError);
}
for &c in cs.iter().rev() {
self.prefix(c)?;
}
Ok(self.cap - self.len())
}
}
#[cfg(test)]
mod tests {
use crate::RollingCharBuffer;
use crate::rolling_char_buffer::RollingCharBufferError;
#[test]
fn test_buffer_is_empty() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.len(), 0);
assert!(rb.is_empty());
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
}
#[test]
fn test_buffer_stores_one_char() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('t'), Ok(()));
assert_eq!(rb.len(), 1);
assert!(!rb.is_empty());
assert_eq!(rb.read(), Ok('t'));
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert!(rb.is_empty());
}
#[test]
fn test_buffer_fill() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.push('e'), Ok(()));
assert_eq!(rb.push('f'), Err(RollingCharBufferError::BufferFullError));
assert!(!rb.is_empty());
assert_eq!(rb.len(), 5);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.push('f'), Ok(()));
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.len(), 4);
assert!(!rb.is_empty());
}
#[test]
fn test_buffer_clear() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.push('e'), Ok(()));
assert_eq!(rb.push('f'), Err(RollingCharBufferError::BufferFullError));
assert_eq!(rb.len(), 5);
assert!(!rb.is_empty());
rb.clear();
assert!(rb.is_empty());
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert_eq!(rb.len(), 0);
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.len(), 2);
assert!(!rb.is_empty());
rb.clear();
assert!(rb.is_empty());
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert_eq!(rb.len(), 0);
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.len(), 1);
}
#[test]
fn test_buffer_rolls() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.len(), 0);
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.len(), 1);
assert_eq!(rb.push('e'), Ok(()));
assert_eq!(rb.len(), 2);
assert!(!rb.is_empty());
assert_eq!(rb.push('f'), Ok(()));
assert_eq!(rb.len(), 3);
assert!(!rb.is_empty());
assert_eq!(rb.push('g'), Ok(()));
assert_eq!(rb.len(), 4);
assert_eq!(rb.push('h'), Ok(()));
assert_eq!(rb.len(), 5);
assert_eq!(rb.push('i'), Err(RollingCharBufferError::BufferFullError));
assert!(!rb.is_empty());
assert_eq!(rb.read(), Ok('d'));
assert_eq!(rb.len(), 4);
assert_eq!(rb.push('i'), Ok(()));
assert_eq!(rb.len(), 5);
assert_eq!(rb.read(), Ok('e'));
assert_eq!(rb.len(), 4);
assert_eq!(rb.read(), Ok('f'));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('g'));
assert_eq!(rb.len(), 2);
assert_eq!(rb.read(), Ok('h'));
assert_eq!(rb.len(), 1);
assert_eq!(rb.read(), Ok('i'));
assert_eq!(rb.len(), 0);
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert!(rb.is_empty());
assert_eq!(rb.push('j'), Ok(()));
assert_eq!(rb.len(), 1);
assert!(!rb.is_empty());
}
#[test]
fn test_buffer_extends() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.len(), 1);
assert_eq!(rb.extend(&['b', 'c', 'd']), Ok(1));
assert_eq!(rb.len(), 4);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.len(), 2);
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.len(), 1);
assert_eq!(rb.extend(&['e', 'f', 'g', 'h']), Ok(0));
assert_eq!(rb.len(), 5);
assert_eq!(
rb.extend(&['i', 'j', 'k', 'l']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.read(), Ok('d'));
assert_eq!(rb.len(), 4);
assert_eq!(rb.read(), Ok('e'));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('f'));
assert_eq!(rb.len(), 2);
assert_eq!(
rb.extend(&['i', 'j', 'k', 'l']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.read(), Ok('g'));
assert_eq!(rb.len(), 1);
assert_eq!(rb.read(), Ok('h'));
assert_eq!(rb.len(), 0);
assert!(rb.is_empty());
}
#[test]
fn test_buffer_prepends_one_char() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.prefix('t'), Ok(()));
assert_eq!(rb.len(), 1);
assert!(!rb.is_empty());
assert_eq!(rb.read(), Ok('t'));
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert!(rb.is_empty());
}
#[test]
fn test_buffer_adds_prepends_one_char() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('t'), Ok(()));
assert_eq!(rb.prefix('i'), Ok(()));
assert_eq!(rb.len(), 2);
assert!(!rb.is_empty());
assert_eq!(rb.read(), Ok('i'));
assert_eq!(rb.read(), Ok('t'));
assert_eq!(rb.read(), Err(RollingCharBufferError::BufferEmptyError));
assert!(rb.is_empty());
}
#[test]
fn test_buffer_extends_and_prepend() {
let mut rb = RollingCharBuffer::<8>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.len(), 1);
assert_eq!(rb.extend(&['b', 'c', 'd']), Ok(4));
assert_eq!(rb.len(), 4);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.len(), 2);
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.len(), 1);
assert_eq!(rb.extend(&['e', 'f', 'g', 'h']), Ok(3));
assert_eq!(rb.prepend(&['a', 'b', 'c']), Ok(0));
assert_eq!(rb.len(), 8);
assert_eq!(
rb.extend(&['i', 'j', 'k', 'l']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.len(), 7);
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.len(), 6);
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.len(), 5);
assert_eq!(rb.extend(&['i', 'j']), Ok(1));
assert_eq!(rb.prepend(&['c']), Ok(0));
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.len(), 7);
assert_eq!(rb.read(), Ok('d'));
assert_eq!(rb.len(), 6);
assert_eq!(rb.read(), Ok('e'));
assert_eq!(rb.len(), 5);
assert_eq!(rb.read(), Ok('f'));
assert_eq!(rb.len(), 4);
assert_eq!(rb.read(), Ok('g'));
assert_eq!(rb.len(), 3);
assert_eq!(rb.read(), Ok('h'));
assert_eq!(rb.len(), 2);
assert_eq!(rb.read(), Ok('i'));
assert_eq!(rb.len(), 1);
assert_eq!(rb.read(), Ok('j'));
assert_eq!(rb.len(), 0);
assert!(rb.is_empty());
}
#[test]
fn test_single_capacity_buffer() {
let mut rb = RollingCharBuffer::<1>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.len(), 1);
assert!(rb.is_full());
assert_eq!(rb.push('b'), Err(RollingCharBufferError::BufferFullError));
assert_eq!(rb.read(), Ok('a'));
assert!(rb.is_empty());
}
#[test]
fn test_buffer_pop_operations() {
let mut rb = RollingCharBuffer::<5>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.pop(), Ok('c'));
assert_eq!(rb.pop(), Ok('b'));
assert_eq!(rb.pop(), Ok('a'));
assert_eq!(rb.pop(), Err(RollingCharBufferError::BufferEmptyError));
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.push('e'), Ok(()));
assert_eq!(rb.read(), Ok('d')); assert_eq!(rb.pop(), Ok('e')); assert!(rb.is_empty());
}
#[test]
fn test_buffer_wrap_around() {
let mut rb = RollingCharBuffer::<3>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.read(), Ok('b'));
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.read(), Ok('d'));
assert!(rb.is_empty());
}
#[test]
fn test_alternating_push_pop() {
let mut rb = RollingCharBuffer::<3>::new();
assert_eq!(rb.push('a'), Ok(()));
assert_eq!(rb.pop(), Ok('a'));
assert_eq!(rb.push('b'), Ok(()));
assert_eq!(rb.pop(), Ok('b'));
assert_eq!(rb.push('c'), Ok(()));
assert_eq!(rb.push('d'), Ok(()));
assert_eq!(rb.pop(), Ok('d'));
assert_eq!(rb.pop(), Ok('c'));
assert!(rb.is_empty());
}
#[test]
fn test_alternating_prefix_read() {
let mut rb = RollingCharBuffer::<3>::new();
assert_eq!(rb.prefix('a'), Ok(()));
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.prefix('b'), Ok(()));
assert_eq!(rb.prefix('c'), Ok(()));
assert_eq!(rb.read(), Ok('c'));
assert_eq!(rb.read(), Ok('b'));
assert!(rb.is_empty());
}
#[test]
fn test_extend_error_handling() {
let mut rb = RollingCharBuffer::<3>::new();
assert_eq!(
rb.extend(&['a', 'b', 'c', 'd']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.extend(&['a', 'b', 'c']), Ok(0));
assert_eq!(
rb.extend(&['e']),
Err(RollingCharBufferError::BufferFullError)
);
rb.clear();
assert_eq!(rb.extend(&['a', 'b']), Ok(1));
assert_eq!(rb.len(), 2);
}
#[test]
fn test_prepend_error_handling() {
let mut rb = RollingCharBuffer::<3>::new();
assert_eq!(
rb.prepend(&['a', 'b', 'c', 'd']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.extend(&['a', 'b', 'c']), Ok(0));
assert_eq!(
rb.prepend(&['e']),
Err(RollingCharBufferError::BufferFullError)
);
assert_eq!(rb.read(), Ok('a'));
assert_eq!(rb.prepend(&['e']), Ok(0));
assert_eq!(rb.len(), 3);
}
#[test]
fn test_large_buffer_operations() {
let mut rb = RollingCharBuffer::<1000>::new();
for i in 0..500 {
let c = char::from_u32(97 + (i % 26) as u32).unwrap(); assert_eq!(rb.push(c), Ok(()));
}
assert_eq!(rb.len(), 500);
for _ in 0..250 {
assert!(rb.read().is_ok());
}
assert_eq!(rb.len(), 250);
for i in 0..750 {
let c = char::from_u32(65 + (i % 26) as u32).unwrap(); assert_eq!(rb.push(c), Ok(()));
}
assert_eq!(rb.len(), 1000);
assert!(rb.is_full());
}
}