irox_tools/buf/
round_u8.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5#![allow(clippy::indexing_slicing)]
6#![allow(clippy::unwrap_used)]
7
8use crate::buf::Buffer;
9use core::ops::Index;
10use irox_bits::{Bits, BitsError, BitsErrorKind, Error, ErrorKind, MutBits};
11
12///
13/// Double-ended circular Buffer.  Basically a fixed size [`std::collections::VecDeque`]
14pub struct RoundU8Buffer<const N: usize> {
15    buf: [u8; N],
16    head: usize,
17    tail: usize,
18    size: usize,
19    mod_count: u32,
20}
21
22impl<const N: usize> RoundU8Buffer<N> {
23    pub fn pop_n_front<const L: usize>(&mut self) -> Option<[u8; L]> {
24        if self.size < L || N < L {
25            return None;
26        }
27        self.size -= L;
28        self.mod_count = self.mod_count.wrapping_add(1);
29        let mut out = [0u8; L];
30        for out in out.iter_mut().take(L) {
31            *out = self.buf[self.head];
32            // move the head pointer forward one
33            // unless head == tail
34            if self.head != self.tail {
35                self.head += 1;
36            }
37            // if head >= N, then wrap around
38            if self.head >= N {
39                self.head = 0;
40            }
41        }
42        Some(out)
43    }
44
45    ///
46    /// Returns the free space (capacity) in this buffer.
47    pub const fn available_capacity(&self) -> usize {
48        N - self.size
49    }
50
51    /// Provides the function with a mutable ref to the inner buffer.  The function
52    /// MUST return the updated "used" size of the buffer.
53    pub fn as_ref_mut<F: FnMut(usize, &mut [u8]) -> Result<usize, BitsError>>(
54        &mut self,
55        mut func: F,
56    ) -> Result<(), BitsError> {
57        let used = func(self.size, &mut self.buf)?;
58        self.size = used;
59        Ok(())
60    }
61
62    pub fn as_ref_used(&self) -> (&[u8], &[u8]) {
63        let a_start = self.head;
64        let a_end = (a_start + self.size).min(N);
65        let a_used = a_end - a_start;
66        let b_start = 0;
67        let b_end = b_start + self.size - a_used;
68        let a = self.buf.get(a_start..a_end).unwrap_or_default();
69        let b = self.buf.get(b_start..b_end).unwrap_or_default();
70        (a, b)
71    }
72
73    pub fn as_ref_mut_available(&mut self) -> &mut [u8] {
74        if self.is_full() {
75            return &mut [];
76        } else if self.is_empty() {
77            self.clear();
78            return &mut self.buf;
79        }
80        if self.tail > self.head {
81            // no wrap.
82            let a_start = (self.tail + 1).min(N);
83            let a_end = N;
84            let a_avail = a_end - a_start;
85            if a_avail > 0 {
86                return self.buf.get_mut(a_start..a_end).unwrap_or_default();
87            }
88            return self.buf.get_mut(0..self.head).unwrap_or_default();
89        }
90        // probably wraps.
91        self.buf.get_mut(self.tail..self.head).unwrap_or_default()
92    }
93
94    ///
95    /// Appends all values into this buffer
96    pub fn append_all(&mut self, vals: &[u8]) -> Result<(), BitsError> {
97        for v in vals {
98            if self.push_back(*v).is_err() {
99                return Err(BitsErrorKind::OutOfMemory.into());
100            };
101        }
102        Ok(())
103    }
104
105    ///
106    /// Appends some of the data into this buffer.  Returns the number of bytes
107    /// successfully appended - which may be less than the total available.
108    pub fn append_some(&mut self, vals: &[u8]) -> usize {
109        let mut used = 0;
110        for v in vals {
111            if self.push_back(*v).is_err() {
112                return used;
113            }
114            used += 1;
115        }
116        used
117    }
118
119    ///
120    /// Advances the head pointer the specified amount, up to the filled length of this buffer.
121    pub fn consume(&mut self, amt: usize) {
122        if amt >= self.size {
123            return self.clear();
124        }
125        self.head += amt;
126        self.size -= amt;
127        if self.head >= N {
128            self.head -= N;
129        }
130    }
131
132    ///
133    /// Marks some of the internal buffer space as used by advancing the tail pointer by
134    /// the specified amount
135    pub fn mark_some_used(&mut self, used: usize) -> Result<(), BitsError> {
136        if self.size + used > N {
137            return Err(BitsErrorKind::OutOfMemory.into());
138        }
139        self.size += used;
140        self.tail += used;
141        if self.tail >= N {
142            self.tail -= N;
143        }
144        Ok(())
145    }
146
147    ///
148    /// Limits the internal returned buffer to the specified amount by clipping the
149    /// 'used length' parameter
150    pub fn limit(&mut self, limit: usize) -> Result<(), BitsError> {
151        if limit > N || self.size < limit {
152            return BitsErrorKind::InvalidInput.err("Invalid limit");
153        }
154        self.size = limit;
155        Ok(())
156    }
157}
158
159impl<const N: usize> From<[u8; N]> for RoundU8Buffer<N> {
160    fn from(value: [u8; N]) -> Self {
161        Self {
162            buf: value,
163            head: 0,
164            tail: N - 1,
165            size: N,
166            mod_count: 0,
167        }
168    }
169}
170
171/// Circular buffer iterator, just calls `pop_front()` repeatedly
172pub struct RoundU8BufferIter<const N: usize> {
173    buf: RoundU8Buffer<N>,
174}
175
176impl<const N: usize> Iterator for RoundU8BufferIter<N> {
177    type Item = u8;
178
179    fn next(&mut self) -> Option<Self::Item> {
180        self.buf.pop_front()
181    }
182}
183
184impl<const N: usize> IntoIterator for RoundU8Buffer<N> {
185    type Item = u8;
186    type IntoIter = RoundU8BufferIter<N>;
187
188    fn into_iter(self) -> Self::IntoIter {
189        RoundU8BufferIter { buf: self }
190    }
191}
192
193impl<const N: usize> Default for RoundU8Buffer<N> {
194    fn default() -> Self {
195        RoundU8Buffer {
196            buf: [Default::default(); N],
197            head: 0,
198            tail: 0,
199            size: 0,
200            mod_count: 0,
201        }
202    }
203}
204
205impl<const N: usize> Buffer<u8> for RoundU8Buffer<N> {
206    fn get(&self, index: usize) -> Option<&u8> {
207        if index >= N || index >= self.size {
208            return None;
209        }
210        Some(&self.buf[index])
211    }
212
213    fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
214        if index >= N || index >= self.size {
215            return None;
216        }
217        Some(&mut self.buf[index])
218    }
219
220    fn capacity(&self) -> usize {
221        N
222    }
223
224    fn len(&self) -> usize {
225        self.size
226    }
227
228    fn clear(&mut self) {
229        self.head = 0;
230        self.tail = 0;
231        self.size = 0;
232        self.mod_count = self.mod_count.wrapping_add(1);
233        self.buf.fill(0);
234    }
235
236    fn front(&self) -> Option<&u8> {
237        Some(&self.buf[self.head])
238    }
239
240    fn front_mut(&mut self) -> Option<&mut u8> {
241        Some(&mut self.buf[self.head])
242    }
243
244    fn back(&self) -> Option<&u8> {
245        Some(&self.buf[self.tail])
246    }
247
248    fn back_mut(&mut self) -> Option<&mut u8> {
249        Some(&mut self.buf[self.tail])
250    }
251
252    fn pop_front(&mut self) -> Option<u8> {
253        if self.size == 0 || N == 0 {
254            return None;
255        }
256        self.size -= 1;
257        self.mod_count = self.mod_count.wrapping_add(1);
258
259        let out = Some(self.buf[self.head]);
260        self.buf[self.head] = 0;
261        // move the head pointer forward one
262        if self.size > 0 {
263            self.head += 1;
264        }
265        // if head >= N, then wrap around
266        if self.head >= N {
267            self.head = 0;
268        }
269        out
270    }
271
272    fn pop_back(&mut self) -> Option<u8> {
273        if self.size == 0 || N == 0 {
274            // empty
275            return None;
276        }
277        let out = Some(self.buf[self.tail]);
278        self.buf[self.tail] = 0;
279        self.mod_count = self.mod_count.wrapping_add(1);
280
281        self.size -= 1;
282        // move the tail pointer back
283        // unless head == tail
284        if self.head != self.tail {
285            // if tail == 0, wrap around
286            if self.tail == 0 {
287                self.tail = N - 1;
288            } else {
289                self.tail -= 1;
290            }
291        }
292        out
293    }
294
295    fn push_front(&mut self, value: u8) -> Result<(), u8> {
296        if self.size == N || N == 0 {
297            // full
298            Err(value)
299        } else if self.size == 0 {
300            self.mod_count = self.mod_count.wrapping_add(1);
301
302            self.head = 0;
303            self.tail = 0;
304            self.buf[0] = value;
305            self.size = 1;
306            Ok(())
307        } else {
308            self.mod_count = self.mod_count.wrapping_add(1);
309
310            if self.head == 0 {
311                self.head = N - 1;
312            }
313            self.buf[self.head] = value;
314            self.size += 1;
315            Ok(())
316        }
317    }
318
319    fn push_back(&mut self, value: u8) -> Result<(), u8> {
320        if self.size == N || N == 0 {
321            // full
322            Err(value)
323        } else if self.size == 0 {
324            self.mod_count = self.mod_count.wrapping_add(1);
325
326            // empty
327            self.head = 0;
328            self.tail = 0;
329            self.size = 1;
330            self.buf[0] = value;
331            Ok(())
332        } else {
333            self.mod_count = self.mod_count.wrapping_add(1);
334
335            // mixed
336            self.size += 1;
337            self.tail += 1;
338            if self.tail == N {
339                self.tail = 0;
340            }
341            self.buf[self.tail] = value;
342            Ok(())
343        }
344    }
345}
346
347impl<const N: usize> Bits for RoundU8Buffer<N> {
348    fn next_u8(&mut self) -> Result<Option<u8>, Error> {
349        Ok(self.pop_front())
350    }
351
352    fn read_be_u32(&mut self) -> Result<u32, Error> {
353        let a = self
354            .pop_n_front::<4>()
355            .ok_or_else(|| BitsError::new(BitsErrorKind::UnexpectedEof, "EOF"))?;
356        Ok(u32::from_be_bytes(a))
357    }
358}
359impl<const N: usize> Bits for &mut RoundU8Buffer<N> {
360    fn next_u8(&mut self) -> Result<Option<u8>, Error> {
361        Ok(self.pop_front())
362    }
363
364    fn read_be_u32(&mut self) -> Result<u32, Error> {
365        let a = self
366            .pop_n_front::<4>()
367            .ok_or_else(|| BitsError::new(BitsErrorKind::UnexpectedEof, "EOF"))?;
368        Ok(u32::from_be_bytes(a))
369    }
370}
371
372impl<const N: usize> MutBits for RoundU8Buffer<N> {
373    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
374        self.push_back(val)
375            .map_err(|_| ErrorKind::OutOfMemory.into())
376    }
377}
378
379impl<const N: usize> MutBits for &mut RoundU8Buffer<N> {
380    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
381        self.push_back(val)
382            .map_err(|_| ErrorKind::OutOfMemory.into())
383    }
384}
385
386#[allow(clippy::panic)]
387impl<const N: usize> Index<usize> for RoundU8Buffer<N> {
388    type Output = u8;
389
390    fn index(&self, index: usize) -> &Self::Output {
391        assert!(index < self.size, "{index} >= {}", self.size);
392        let mut offset = self.head + index;
393        if offset >= N {
394            offset -= N;
395        }
396        let Some(val) = self.buf.get(offset) else {
397            panic!("expected value at offset {offset} but was empty!");
398        };
399        val
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use crate::buf::Buffer;
406    use irox_bits::Error;
407
408    macro_rules! assert_empty {
409        ($buf:ident) => {
410            assert_eq!(0, $buf.len());
411            assert_eq!(None, $buf.pop_front());
412            assert_eq!(None, $buf.pop_back());
413        };
414    }
415
416    macro_rules! assert_full {
417        ($buf:ident, $sz:literal, $elem:expr) => {
418            assert_eq!($sz, $buf.len());
419            assert_eq!(Err($elem), $buf.push_back($elem));
420            assert_eq!($sz, $buf.len());
421            assert_eq!(Err($elem), $buf.push_front($elem));
422            assert_eq!($sz, $buf.len());
423        };
424    }
425
426    #[test]
427    pub fn test_push_some() -> Result<(), u32> {
428        let mut buf = crate::buf::RoundU8Buffer::<3>::default();
429        assert_empty!(buf);
430
431        buf.push_back(10)?;
432        assert_eq!(1, buf.len());
433        assert_eq!(0, buf.head);
434        assert_eq!(0, buf.tail);
435
436        buf.push_back(15)?;
437        assert_eq!(2, buf.len());
438        assert_eq!(0, buf.head);
439        assert_eq!(1, buf.tail);
440
441        buf.push_back(20)?;
442        assert_eq!(3, buf.len());
443        assert_eq!(0, buf.head);
444        assert_eq!(2, buf.tail);
445
446        assert_full!(buf, 3, 25);
447
448        assert_eq!(Some(10), buf.pop_front());
449        assert_eq!(2, buf.len());
450        assert_eq!(1, buf.head);
451        assert_eq!(2, buf.tail);
452
453        buf.push_back(30)?;
454        assert_eq!(3, buf.len());
455        assert_eq!(1, buf.head);
456        assert_eq!(0, buf.tail);
457
458        assert_full!(buf, 3, 35);
459
460        assert_eq!(Some(15), buf.pop_front());
461        assert_eq!(2, buf.len());
462        assert_eq!(2, buf.head);
463        assert_eq!(0, buf.tail);
464
465        assert_eq!(Some(20), buf.pop_front());
466        assert_eq!(1, buf.len());
467        assert_eq!(0, buf.head);
468        assert_eq!(0, buf.tail);
469
470        assert_eq!(Some(30), buf.pop_front());
471        assert_eq!(0, buf.len());
472        assert_eq!(0, buf.head);
473        assert_eq!(0, buf.tail);
474
475        assert_empty!(buf);
476        assert_empty!(buf);
477
478        Ok(())
479    }
480
481    #[test]
482    pub fn test_slicing() -> Result<(), Error> {
483        let mut buf = crate::buf::RoundU8Buffer::<10>::default();
484        assert_eq!(0, buf.len());
485        let (a, b) = buf.as_ref_used();
486        assert_eq!(0, a.len());
487        assert_eq!(0, b.len());
488        let avail = buf.as_ref_mut_available();
489        assert_eq!(10, avail.len());
490
491        for i in 0..5 {
492            buf.push_back(i).unwrap();
493        }
494        assert_eq!(5, buf.len());
495
496        let (a, b) = buf.as_ref_used();
497        assert_eq!(5, a.len());
498        assert_eq!(0, b.len());
499        assert_eq!(&[0, 1, 2, 3, 4], a);
500        let avail = buf.as_ref_mut_available();
501        assert_eq!(5, avail.len());
502        assert_eq!(&[0, 0, 0, 0, 0], avail);
503
504        assert_eq!(Some(0), buf.pop_front());
505        assert_eq!(4, buf.len());
506        let (a, b) = buf.as_ref_used();
507        assert_eq!(4, a.len());
508        assert_eq!(0, b.len());
509        assert_eq!(&[1, 2, 3, 4], a);
510        let avail = buf.as_ref_mut_available();
511        assert_eq!(5, avail.len());
512        assert_eq!(&[0, 0, 0, 0, 0], avail);
513
514        buf.append_all(&[5, 6, 7, 8, 9, 10])?;
515        assert_eq!(10, buf.len());
516        let (a, b) = buf.as_ref_used();
517        assert_eq!(9, a.len());
518        assert_eq!(1, b.len());
519        assert_eq!(&[1, 2, 3, 4, 5, 6, 7, 8, 9], a);
520        assert_eq!(&[10], b);
521        let avail = buf.as_ref_mut_available();
522        assert_eq!(0, avail.len());
523
524        assert_eq!(Some(10), buf.pop_back());
525        let avail = buf.as_ref_mut_available();
526        assert_eq!(1, avail.len());
527        assert_eq!(&[0], avail);
528
529        buf.consume(5);
530        assert_eq!(4, buf.len());
531        let (a, b) = buf.as_ref_used();
532        assert_eq!(&[6, 7, 8, 9], a);
533        assert_eq!(0, b.len());
534        buf.consume(4);
535        assert_eq!(0, buf.len());
536
537        let (a, b) = buf.as_ref_used();
538        assert_eq!(0, a.len());
539        assert_eq!(0, b.len());
540        let avail = buf.as_ref_mut_available();
541        assert_eq!(10, avail.len());
542        Ok(())
543    }
544}