gemini_genai_rs/buffer/
mod.rs1pub mod convert;
9pub mod jitter;
10
11pub use convert::{bytes_to_i16, i16_to_bytes, into_shared};
12pub use jitter::{AudioJitterBuffer, BufferState, JitterConfig};
13
14#[derive(Debug)]
35pub struct SpscRing<T>(std::marker::PhantomData<T>);
36
37impl<T: Copy> SpscRing<T> {
38 pub fn channel(capacity: usize) -> (SpscProducer<T>, SpscConsumer<T>) {
45 assert!(capacity > 0, "ring capacity must be > 0");
46 let (producer, consumer) = rtrb::RingBuffer::new(capacity);
47 (SpscProducer(producer), SpscConsumer(consumer))
48 }
49}
50
51pub struct SpscProducer<T>(rtrb::Producer<T>);
53
54pub struct SpscConsumer<T>(rtrb::Consumer<T>);
56
57impl<T: Copy> SpscProducer<T> {
58 pub fn write(&mut self, data: &[T]) -> usize {
63 let (written, _remaining) = self.0.push_partial_slice(data);
64 written.len()
65 }
66
67 pub fn available(&self) -> usize {
69 self.0.slots()
70 }
71
72 pub fn is_full(&self) -> bool {
74 self.0.is_full()
75 }
76
77 pub fn capacity(&self) -> usize {
79 self.0.buffer().capacity()
80 }
81
82 pub fn is_abandoned(&self) -> bool {
85 self.0.is_abandoned()
86 }
87}
88
89impl<T: Copy> SpscConsumer<T> {
90 pub fn read(&mut self, out: &mut [T]) -> usize {
92 let (filled, _unfilled) = self.0.pop_partial_slice(out);
93 filled.len()
94 }
95
96 pub fn len(&self) -> usize {
98 self.0.slots()
99 }
100
101 pub fn is_empty(&self) -> bool {
103 self.0.is_empty()
104 }
105
106 pub fn capacity(&self) -> usize {
108 self.0.buffer().capacity()
109 }
110
111 pub fn clear(&mut self) {
113 let n = self.0.slots();
114 if let Ok(chunk) = self.0.read_chunk(n) {
115 chunk.commit_all();
116 }
117 }
118
119 pub fn is_abandoned(&self) -> bool {
122 self.0.is_abandoned()
123 }
124}
125
126impl<T> std::fmt::Debug for SpscProducer<T> {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 f.debug_struct("SpscProducer")
129 .field("free", &self.0.slots())
130 .field("capacity", &self.0.buffer().capacity())
131 .finish()
132 }
133}
134
135impl<T> std::fmt::Debug for SpscConsumer<T> {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 f.debug_struct("SpscConsumer")
138 .field("len", &self.0.slots())
139 .field("capacity", &self.0.buffer().capacity())
140 .finish()
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn capacity_is_exact() {
150 let (tx, rx) = SpscRing::<i16>::channel(100);
151 assert_eq!(tx.capacity(), 100);
152 assert_eq!(rx.capacity(), 100);
153 assert!(rx.is_empty());
154 assert_eq!(tx.available(), 100);
155 }
156
157 #[test]
158 fn write_and_read() {
159 let (mut tx, mut rx) = SpscRing::<i16>::channel(16);
160 assert_eq!(tx.write(&[1i16, 2, 3, 4, 5]), 5);
161 assert_eq!(rx.len(), 5);
162 let mut out = [0i16; 5];
163 assert_eq!(rx.read(&mut out), 5);
164 assert_eq!(out, [1, 2, 3, 4, 5]);
165 assert!(rx.is_empty());
166 }
167
168 #[test]
169 fn wraparound() {
170 let (mut tx, mut rx) = SpscRing::<i16>::channel(8);
171 tx.write(&[1i16, 2, 3, 4, 5, 6]);
172 let mut out = [0i16; 4];
173 rx.read(&mut out);
174 assert_eq!(out, [1, 2, 3, 4]);
175 assert_eq!(tx.write(&[7i16, 8, 9, 10, 11, 12]), 6);
176 let mut out2 = [0i16; 8];
177 assert_eq!(rx.read(&mut out2), 8);
178 assert_eq!(out2, [5, 6, 7, 8, 9, 10, 11, 12]);
179 }
180
181 #[test]
182 fn overflow_returns_partial() {
183 let (mut tx, _rx) = SpscRing::<i16>::channel(4);
184 assert_eq!(tx.write(&[1i16, 2, 3, 4, 5, 6]), 4);
185 assert!(tx.is_full());
186 }
187
188 #[test]
189 fn underflow_returns_partial() {
190 let (mut tx, mut rx) = SpscRing::<i16>::channel(16);
191 tx.write(&[1i16, 2, 3]);
192 let mut out = [0i16; 10];
193 assert_eq!(rx.read(&mut out), 3);
194 assert_eq!(&out[..3], &[1, 2, 3]);
195 }
196
197 #[test]
198 fn clear_discards_data() {
199 let (mut tx, mut rx) = SpscRing::<i16>::channel(16);
200 tx.write(&[1i16, 2, 3, 4, 5]);
201 assert_eq!(rx.len(), 5);
202 rx.clear();
203 assert!(rx.is_empty());
204 assert_eq!(tx.available(), 16);
205 }
206
207 #[test]
208 fn abandonment_is_visible_to_the_other_half() {
209 let (tx, rx) = SpscRing::<i16>::channel(4);
210 assert!(!tx.is_abandoned());
211 drop(rx);
212 assert!(tx.is_abandoned());
213 }
214
215 #[test]
216 fn halves_move_to_their_threads() {
217 fn is_send<T: Send>() {}
221 is_send::<SpscProducer<i16>>();
222 is_send::<SpscConsumer<i16>>();
223 }
224
225 #[test]
226 fn concurrent_write_read() {
227 let (mut tx, mut rx) = SpscRing::<i16>::channel(1024);
228
229 let writer = std::thread::spawn(move || {
230 let mut total = 0usize;
231 for i in 0..1000 {
232 let chunk: Vec<i16> = (0..16).map(|j| (i * 16 + j) as i16).collect();
233 loop {
234 let w = tx.write(&chunk[total % 16..]);
235 total += w;
236 if total >= (i as usize + 1) * 16 {
237 break;
238 }
239 std::thread::yield_now();
240 }
241 }
242 });
243
244 let reader = std::thread::spawn(move || {
245 let mut total = 0usize;
246 let mut buf = [0i16; 64];
247 while total < 16000 {
248 let r = rx.read(&mut buf);
249 total += r;
250 if r == 0 {
251 std::thread::yield_now();
252 }
253 }
254 total
255 });
256
257 writer.join().unwrap();
258 assert_eq!(reader.join().unwrap(), 16000);
259 }
260
261 #[test]
262 #[should_panic(expected = "capacity must be > 0")]
263 fn zero_capacity_panics() {
264 SpscRing::<i16>::channel(0);
265 }
266}