irox_tools/buf/
fixed.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5#![allow(clippy::indexing_slicing)]
6#![allow(clippy::unwrap_used)]
7use crate::buf::Buffer;
8use crate::iterators::LendingIterator;
9use crate::options::MaybeMap;
10use core::iter::zip;
11use core::ops::{Index, IndexMut};
12use irox_bits::{BitsError, BitsErrorKind, Error, MutBits, WriteToBEBits};
13// pub type StrBuf<const N: usize> = FixedBuf<N, char>;
14
15///
16/// Fixed length stack allocated buffer.  Basically a stack-allocated [`std::vec::Vec`]
17#[derive(Clone)]
18pub struct FixedBuf<const N: usize, T: Sized> {
19    buf: [Option<T>; N],
20    len: usize,
21}
22impl<const N: usize, T: Sized> FixedBuf<N, T> {
23    const NONE_T: Option<T> = None;
24
25    pub const fn new() -> Self {
26        Self {
27            buf: [Self::NONE_T; N],
28            len: 0,
29        }
30    }
31}
32impl<const N: usize, T: Sized> Default for FixedBuf<N, T> {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl<const N: usize> core::fmt::Write for FixedBuf<N, u8> {
39    fn write_str(&mut self, s: &str) -> core::fmt::Result {
40        for b in s.as_bytes() {
41            self.push(*b).map_err(|_| core::fmt::Error)?;
42        }
43        Ok(())
44    }
45}
46impl<const N: usize> core::fmt::Write for FixedBuf<N, char> {
47    fn write_str(&mut self, s: &str) -> core::fmt::Result {
48        for b in s.chars() {
49            self.push(b).map_err(|_| core::fmt::Error)?;
50        }
51        Ok(())
52    }
53}
54
55impl<const N: usize, T: Sized> Buffer<T> for FixedBuf<N, T> {
56    fn get(&self, index: usize) -> Option<&T> {
57        if index >= N || index >= self.len {
58            return None;
59        }
60        self.buf.get(index).maybe_map(Option::as_ref)
61    }
62
63    fn get_mut(&mut self, index: usize) -> Option<&mut T> {
64        if index >= N || index >= self.len {
65            return None;
66        }
67        self.buf.get_mut(index).maybe_map(Option::as_mut)
68    }
69
70    fn capacity(&self) -> usize {
71        N
72    }
73
74    fn len(&self) -> usize {
75        self.len
76    }
77
78    fn clear(&mut self) {
79        self.len = 0
80    }
81
82    fn front(&self) -> Option<&T> {
83        self.get(0)
84    }
85
86    fn front_mut(&mut self) -> Option<&mut T> {
87        self.get_mut(0)
88    }
89
90    fn back(&self) -> Option<&T> {
91        if N == 0 || self.len == 0 {
92            return None;
93        }
94        self.get(self.len - 1)
95    }
96
97    fn back_mut(&mut self) -> Option<&mut T> {
98        if N == 0 || self.len == 0 {
99            return None;
100        }
101        self.get_mut(self.len - 1)
102    }
103
104    fn pop_front(&mut self) -> Option<T> {
105        if N == 0 || self.len == 0 {
106            return None;
107        }
108        let out = self.buf[0].take();
109        for idx in 1..self.len {
110            self.buf[idx - 1] = self.buf[idx].take();
111        }
112        self.len -= 1;
113        out
114    }
115
116    fn pop_back(&mut self) -> Option<T> {
117        if N == 0 || self.len == 0 {
118            return None;
119        }
120        let idx = self.len - 1;
121        self.len -= 1;
122        self.buf[idx].take()
123    }
124
125    fn push_front(&mut self, value: T) -> Result<(), T> {
126        if N == 0 || self.len == N {
127            return Err(value);
128        }
129        for idx in 0..self.len {
130            self.buf[idx + 1] = self.buf[idx].take();
131        }
132        self.buf[0] = Some(value);
133        self.len += 1;
134        Ok(())
135    }
136
137    fn push_back(&mut self, value: T) -> Result<(), T> {
138        if N == 0 || self.len == N {
139            return Err(value);
140        }
141        self.buf[self.len] = Some(value);
142        self.len += 1;
143        Ok(())
144    }
145}
146
147#[allow(clippy::panic)]
148impl<const N: usize, T> Index<usize> for FixedBuf<N, T>
149where
150    T: Default,
151{
152    type Output = T;
153
154    fn index(&self, index: usize) -> &Self::Output {
155        assert!(index < self.len, "index {index} >= len {}", self.len);
156        let Some(Some(val)) = self.buf.get(index) else {
157            panic!("expected value at offset {index} but was empty!");
158        };
159        val
160    }
161}
162#[allow(clippy::panic)]
163impl<const N: usize, T> IndexMut<usize> for FixedBuf<N, T>
164where
165    T: Default,
166{
167    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
168        assert!(index < N, "index {index} >= capacity {N}");
169        if self.buf[index].is_none() {
170            self.len += 1;
171            self.buf[index] = Some(Default::default());
172        }
173        self.buf[index].as_mut().unwrap()
174    }
175}
176
177impl<const N: usize, T: Sized + Default + Copy> FixedBuf<N, T> {
178    pub fn into_buf_default(&mut self) -> [T; N] {
179        let mut out = [T::default(); N];
180        for (i, o) in zip(self.buf.iter_mut(), out.iter_mut()) {
181            if let Some(val) = i.take() {
182                *o = val;
183            }
184        }
185        self.clear();
186        out
187    }
188}
189impl<const N: usize, T: Sized> FixedBuf<N, T> {
190    pub fn iter(&self) -> FixedBufIter<N, T> {
191        FixedBufIter { buf: self, idx: 0 }
192    }
193}
194impl<const N: usize, T: Sized + WriteToBEBits> FixedBuf<N, T> {
195    pub fn write_to<B: MutBits + ?Sized>(&self, out: &mut B) -> Result<usize, BitsError> {
196        for v in self.iter() {
197            WriteToBEBits::write_be_to(v, out)?;
198        }
199        Ok(self.len)
200    }
201}
202pub struct FixedBufIter<'a, const N: usize, T: Sized> {
203    buf: &'a FixedBuf<N, T>,
204    idx: usize,
205}
206
207impl<'a, const N: usize, T: Sized> Iterator for FixedBufIter<'a, N, T> {
208    type Item = &'a T;
209    fn next(&mut self) -> Option<Self::Item> {
210        if let Some(val) = self.buf.get(self.idx) {
211            self.idx += 1;
212            return Some(val);
213        }
214        None
215    }
216}
217impl<const N: usize, T: Sized> DoubleEndedIterator for FixedBufIter<'_, N, T> {
218    fn next_back(&mut self) -> Option<Self::Item> {
219        if self.idx >= self.buf.len {
220            return None;
221        }
222        let idx = self.buf.len().saturating_sub(self.idx).saturating_sub(1);
223
224        self.idx += 1;
225        if let Some(val) = self.buf.get(idx) {
226            return Some(val);
227        }
228        None
229    }
230}
231impl<const N: usize, T: Sized> ExactSizeIterator for FixedBufIter<'_, N, T> {
232    fn len(&self) -> usize {
233        self.buf.len()
234    }
235}
236
237pub struct FixedBufIterMut<'a, const N: usize, T: Sized> {
238    buf: &'a mut FixedBuf<N, T>,
239    idx: usize,
240}
241
242impl<const N: usize, T: Sized> LendingIterator for FixedBufIterMut<'_, N, T> {
243    type Item<'b>
244        = &'b mut T
245    where
246        Self: 'b;
247
248    fn next_ref(&mut self) -> Option<Self::Item<'_>> {
249        if let Some(val) = self.buf.get_mut(self.idx) {
250            self.idx += 1;
251            return Some(val);
252        }
253        None
254    }
255}
256
257impl<const N: usize> MutBits for &mut FixedBuf<N, u8> {
258    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
259        if self.push_back(val).is_err() {
260            return Err(BitsErrorKind::UnexpectedEof.into());
261        }
262        Ok(())
263    }
264}
265impl<const N: usize> MutBits for FixedBuf<N, u8> {
266    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
267        if self.push_back(val).is_err() {
268            return Err(BitsErrorKind::UnexpectedEof.into());
269        }
270        Ok(())
271    }
272}