discoid/
lib.rs

1use std::fmt;
2
3/// Represents errors that can occur when interacting with the CircularBuffer.
4#[derive(Debug, PartialEq)]
5pub enum BufferError {
6    /// Error returned when attempting to push to a full buffer.
7    BufferFull,
8}
9
10impl fmt::Display for BufferError {
11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        match self {
13            Self::BufferFull => write!(f, "circular buffer: cannot push, buffer is full"),
14        }
15    }
16}
17
18impl std::error::Error for BufferError {}
19
20#[derive(Debug, Clone)]
21pub struct CircularBuffer<T> {
22    buffer: Vec<Option<T>>,
23    front: usize,
24    rear: usize,
25    capacity: usize,
26    len: usize,
27}
28
29impl<T> CircularBuffer<T> {
30    /// Creates a new CircularBuffer with the specified capacity.
31    pub fn new(size: usize) -> Self {
32        let mut buffer = Vec::with_capacity(size);
33        for _ in 0..size {
34            buffer.push(None);
35        }
36        CircularBuffer {
37            buffer,
38            front: 0,
39            rear: 0,
40            capacity: size,
41            len: 0,
42        }
43    }
44
45    /// Adds an element to the rear of the buffer.
46    /// Returns `Ok(())` if successful or `Err(BufferError::BufferFull)` if the buffer is full.
47    pub fn push(&mut self, value: T) -> Result<(), BufferError> {
48        if self.is_full() {
49            return Err(BufferError::BufferFull);
50        }
51
52        self.buffer[self.rear] = Some(value);
53        self.rear = (self.rear + 1) % self.capacity;
54        self.len += 1;
55        Ok(())
56    }
57
58    /// Removes and returns the element from the front of the buffer.
59    pub fn pop(&mut self) -> Option<T> {
60        if self.is_empty() {
61            return None;
62        }
63
64        let value = self.buffer[self.front]
65            .take()
66            .expect("Buffer invariant violated: None value in non-empty buffer");
67        self.front = (self.front + 1) % self.capacity;
68        self.len -= 1;
69        Some(value)
70    }
71
72    /// Returns the number of elements in the buffer.
73    pub fn len(&self) -> usize {
74        self.len
75    }
76
77    /// Checks if the buffer is empty.
78    pub fn is_empty(&self) -> bool {
79        self.len == 0
80    }
81
82    /// Checks if the buffer is full.
83    pub fn is_full(&self) -> bool {
84        self.len == self.capacity
85    }
86
87    /// Retrieves a reference to the element at the given index.
88    /// Returns `Some(&T)` if the index is within bounds or `None` otherwise.
89    pub fn get(&self, index: usize) -> Option<&T> {
90        if index >= self.len {
91            return None;
92        }
93
94        let absolute_index = (self.front + index) % self.capacity;
95        self.buffer[absolute_index].as_ref()
96    }
97
98    /// Removes multiple elements from the front of the buffer.
99    pub fn remove_multiple(&mut self, count: usize) {
100        let count_to_remove = if count > self.len { self.len } else { count };
101
102        for _ in 0..count_to_remove {
103            self.buffer[self.front] = None;
104            self.front = (self.front + 1) % self.capacity;
105            self.len -= 1;
106        }
107    }
108}
109
110/// Iterator that consumes the CircularBuffer and yields its elements in order.
111pub struct CircularBufferIntoIter<T> {
112    buffer: Vec<Option<T>>,
113    front: usize,
114    capacity: usize,
115    current: usize,
116    remaining: usize,
117}
118
119impl<T> Iterator for CircularBufferIntoIter<T> {
120    type Item = T;
121
122    fn next(&mut self) -> Option<Self::Item> {
123        if self.remaining == 0 {
124            return None;
125        }
126
127        let index = (self.front + self.current) % self.capacity;
128        self.current += 1;
129        self.remaining -= 1;
130
131        self.buffer[index].take()
132    }
133}
134
135impl<T> IntoIterator for CircularBuffer<T> {
136    type Item = T;
137    type IntoIter = CircularBufferIntoIter<T>;
138
139    fn into_iter(self) -> Self::IntoIter {
140        CircularBufferIntoIter {
141            buffer: self.buffer,
142            front: self.front,
143            capacity: self.capacity,
144            current: 0,
145            remaining: self.len,
146        }
147    }
148}