1use rusty_esp_core::error::{Error, Result};
12use rusty_esp_core::pcm::PcmFormat;
13
14#[derive(Debug)]
16pub struct RingBuffer<'m> {
17 buf: &'m mut [u8],
18 frame_bytes: usize,
19 cap: usize,
21 head: usize,
23 len: usize,
25 pub dropped_frames: u64,
27 pub high_water_frames: usize,
29}
30
31impl<'m> RingBuffer<'m> {
32 pub fn new(buf: &'m mut [u8], format: PcmFormat) -> Result<Self> {
35 let frame_bytes = format.frame_bytes();
36 let cap = (buf.len() / frame_bytes) * frame_bytes;
37 if cap == 0 {
38 return Err(Error::BufferTooSmall {
39 needed: frame_bytes,
40 });
41 }
42 Ok(RingBuffer {
43 buf,
44 frame_bytes,
45 cap,
46 head: 0,
47 len: 0,
48 dropped_frames: 0,
49 high_water_frames: 0,
50 })
51 }
52
53 #[must_use]
55 pub fn capacity_frames(&self) -> usize {
56 self.cap / self.frame_bytes
57 }
58
59 #[must_use]
61 pub fn available_frames(&self) -> usize {
62 self.len / self.frame_bytes
63 }
64
65 #[must_use]
67 pub fn free_frames(&self) -> usize {
68 (self.cap - self.len) / self.frame_bytes
69 }
70
71 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 self.len == 0
75 }
76
77 pub fn clear(&mut self) {
79 self.head = 0;
80 self.len = 0;
81 }
82
83 fn check_frames(&self, data: &[u8]) -> Result<()> {
84 if data.len() % self.frame_bytes != 0 {
85 return Err(Error::InvalidGeometry);
86 }
87 if data.len() > self.cap {
88 return Err(Error::BufferTooSmall { needed: data.len() });
89 }
90 Ok(())
91 }
92
93 fn write_at_tail(&mut self, data: &[u8]) {
94 let tail = (self.head + self.len) % self.cap;
95 let first = (self.cap - tail).min(data.len());
96 self.buf[tail..tail + first].copy_from_slice(&data[..first]);
97 let rest = data.len() - first;
98 if rest > 0 {
99 self.buf[..rest].copy_from_slice(&data[first..]);
100 }
101 self.len += data.len();
102 let frames = self.len / self.frame_bytes;
103 if frames > self.high_water_frames {
104 self.high_water_frames = frames;
105 }
106 }
107
108 pub fn push(&mut self, data: &[u8]) -> Result<()> {
112 self.check_frames(data)?;
113 if data.len() > self.cap - self.len {
114 self.dropped_frames += (data.len() / self.frame_bytes) as u64;
115 return Err(Error::Busy);
116 }
117 self.write_at_tail(data);
118 Ok(())
119 }
120
121 pub fn push_overwrite(&mut self, data: &[u8]) -> Result<usize> {
125 self.check_frames(data)?;
126 let need = data.len().saturating_sub(self.cap - self.len);
127 if need > 0 {
128 self.head = (self.head + need) % self.cap;
129 self.len -= need;
130 let frames = need / self.frame_bytes;
131 self.dropped_frames += frames as u64;
132 self.write_at_tail(data);
133 return Ok(frames);
134 }
135 self.write_at_tail(data);
136 Ok(0)
137 }
138
139 pub fn pop(&mut self, out: &mut [u8]) -> usize {
142 let want = (out.len() / self.frame_bytes) * self.frame_bytes;
143 let n = want.min(self.len);
144 if n == 0 {
145 return 0;
146 }
147 let first = (self.cap - self.head).min(n);
148 out[..first].copy_from_slice(&self.buf[self.head..self.head + first]);
149 if n > first {
150 out[first..n].copy_from_slice(&self.buf[..n - first]);
151 }
152 self.head = (self.head + n) % self.cap;
153 self.len -= n;
154 n
155 }
156
157 pub fn pop_exact(&mut self, out: &mut [u8]) -> bool {
161 if out.len() % self.frame_bytes != 0 || out.len() > self.len || out.is_empty() {
162 return false;
163 }
164 self.pop(out);
165 true
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 fn fmt() -> PcmFormat {
174 PcmFormat::PCM16_48K_STEREO }
176
177 #[test]
178 fn wraps_and_counts() {
179 let mut mem = [0u8; 18]; let mut r = RingBuffer::new(&mut mem, fmt()).unwrap();
181 assert_eq!(r.capacity_frames(), 4);
182 r.push(&[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]).unwrap();
183 let mut out = [0u8; 8];
184 assert_eq!(r.pop(&mut out), 8);
185 assert_eq!(out, [1, 1, 1, 1, 2, 2, 2, 2]);
186 r.push(&[4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]).unwrap();
188 assert_eq!(r.available_frames(), 4);
189 assert_eq!(r.free_frames(), 0);
190 assert_eq!(r.high_water_frames, 4);
191 assert_eq!(r.push(&[9, 9, 9, 9]).err(), Some(Error::Busy));
193 assert_eq!(r.dropped_frames, 1);
194 let mut all = [0u8; 16];
195 assert_eq!(r.pop(&mut all), 16);
196 assert_eq!(all, [3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]);
197 assert!(r.is_empty());
198 assert_eq!(r.pop(&mut all), 0);
199 }
200
201 #[test]
202 fn overwrite_discards_oldest() {
203 let mut mem = [0u8; 12];
204 let mut r = RingBuffer::new(&mut mem, fmt()).unwrap();
205 r.push(&[1, 1, 1, 1, 2, 2, 2, 2]).unwrap();
206 assert_eq!(r.push_overwrite(&[3, 3, 3, 3, 4, 4, 4, 4]).unwrap(), 1);
207 assert_eq!(r.dropped_frames, 1);
208 let mut out = [0u8; 12];
209 assert_eq!(r.pop(&mut out), 12);
210 assert_eq!(out, [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]);
211 }
212
213 #[test]
214 fn pop_exact_waits_for_a_block() {
215 let mut mem = [0u8; 16];
216 let mut r = RingBuffer::new(&mut mem, fmt()).unwrap();
217 r.push(&[7; 4]).unwrap();
218 let mut out = [0u8; 8];
219 assert!(!r.pop_exact(&mut out));
220 r.push(&[8; 4]).unwrap();
221 assert!(r.pop_exact(&mut out));
222 assert_eq!(out, [7, 7, 7, 7, 8, 8, 8, 8]);
223 }
224
225 #[test]
226 fn rejects_misaligned_and_oversize() {
227 let mut mem = [0u8; 16];
228 let mut r = RingBuffer::new(&mut mem, fmt()).unwrap();
229 assert_eq!(r.push(&[0; 6]).err(), Some(Error::InvalidGeometry));
230 assert_eq!(
231 r.push(&[0; 20]).err(),
232 Some(Error::BufferTooSmall { needed: 20 })
233 );
234 let mut tiny = [0u8; 3];
235 assert_eq!(
236 RingBuffer::new(&mut tiny, fmt()).err(),
237 Some(Error::BufferTooSmall { needed: 4 })
238 );
239 }
240}