Skip to main content

irox_tools/buf/
fixed_u8.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 core::ops::{Index, IndexMut};
10use core::str::Utf8Error;
11use irox_bits::{Bits, BitsErrorKind, Error, MutBits, ReadFromBEBits, WriteToBEBits};
12
13pub type StrBuf<const N: usize> = FixedU8Buf<N>;
14
15///
16/// Fixed length stack allocated buffer.  Basically a stack-allocated [`Vec`]
17#[derive(Clone)]
18pub struct FixedU8Buf<const N: usize> {
19    buf: [u8; N],
20    len: usize,
21}
22impl<const N: usize> FixedU8Buf<N> {
23    pub const fn new() -> Self {
24        Self {
25            buf: [0u8; N],
26            len: 0,
27        }
28    }
29
30    ///
31    /// Returns the inner buffer, consuming the object.
32    pub fn take(self) -> [u8; N] {
33        self.buf
34    }
35
36    ///
37    /// Returns a copy of the full buffer
38    pub fn as_buf_default(&mut self) -> [u8; N] {
39        let out = core::mem::replace(&mut self.buf, [0u8; N]);
40        self.clear();
41        out
42    }
43
44    ///
45    /// Returns an iterator to the data in this buffer
46    pub fn iter(&self) -> FixedU8BufIter<'_, N> {
47        FixedU8BufIter { buf: self, idx: 0 }
48    }
49
50    ///
51    /// Returns the used length of this buffer.
52    pub const fn len(&self) -> usize {
53        self.len
54    }
55
56    ///
57    /// Returns true if this buffer is empty.
58    pub const fn is_empty(&self) -> bool {
59        self.len == 0
60    }
61
62    ///
63    /// Returns a slice to only the used portion of this buffer
64    pub fn as_ref_used(&self) -> &[u8] {
65        self.as_ref()
66    }
67
68    ///
69    /// Returns a mut slice to the full capacity of this buffer.  If you change the used
70    /// length of the internal buffer, ensure you call [`update_length`] to correct the
71    /// internal length tracking.
72    pub fn as_mut_full(&mut self) -> &mut [u8] {
73        self.as_mut()
74    }
75
76    ///
77    /// Returns a mut slice to only the used capacity of this buffer.
78    pub fn as_mut_used(&mut self) -> &mut [u8] {
79        &mut self.buf[0..self.len]
80    }
81
82    ///
83    /// Manually updates the "used length" of the buffer.  This is expected to be used in
84    /// concert with [`as_mut_full`] when loading data into the buffer.
85    pub fn update_length(&mut self, new_len: usize) -> Result<(), Error> {
86        if new_len > N {
87            return Err(BitsErrorKind::OutOfMemory.into());
88        }
89        self.len = new_len;
90        Ok(())
91    }
92
93    ///
94    /// Pushes a single unicode character into this buffer.
95    pub fn push_char(&mut self, c: char) -> Result<(), Error> {
96        let mut buf = [0u8; 4];
97        let used = c.encode_utf8(&mut buf).len();
98        if self.len + used > N {
99            return Err(BitsErrorKind::OutOfMemory.into());
100        }
101
102        Ok(())
103    }
104
105    ///
106    /// Appends the specified slice to this buffer.
107    pub fn append(&mut self, buf: &[u8]) -> Result<(), Error> {
108        if self.len + buf.len() > N {
109            return Err(BitsErrorKind::OutOfMemory.into());
110        }
111        let start = self.len;
112        let end = start + buf.len();
113        self.buf[start..end].copy_from_slice(buf);
114        self.len = end;
115        Ok(())
116    }
117
118    ///
119    /// Returns the contents of this buffer as a str
120    pub fn as_str(&self) -> Result<&str, Utf8Error> {
121        core::str::from_utf8(self.as_ref_used())
122    }
123    ///
124    /// Returns the contents of this buffer as a mutable str
125    pub fn as_str_mut(&mut self) -> Result<&mut str, Utf8Error> {
126        core::str::from_utf8_mut(self.as_mut_used())
127    }
128
129    ///
130    /// Parses a string representation of a f64 stored within this buffer with
131    /// [`f64::from_str`]
132    pub fn as_f64(&self) -> Result<f64, core::num::ParseFloatError> {
133        let s = self.as_str().unwrap_or_default();
134        core::str::FromStr::from_str(s)
135    }
136
137    ///
138    /// Basic in-place swap.
139    pub fn reverse(&mut self) {
140        let mut i = 0;
141        let mut j = self.len - 1;
142        while i < j {
143            self.buf.swap(i, j);
144            i += 1;
145            j -= 1;
146        }
147    }
148
149    pub fn from_slice(buf: &[u8]) -> Self {
150        let mut out = Self::new();
151        buf.iter().take(N).for_each(|b| {
152            let _ = out.push_back(*b);
153        });
154        out
155    }
156}
157impl<const N: usize> WriteToBEBits for FixedU8Buf<N> {
158    fn write_be_to<T: MutBits + ?Sized>(&self, bits: &mut T) -> Result<usize, Error> {
159        bits.write_be_u32_blob(self.as_ref_used())?;
160        Ok(self.len + 4)
161    }
162}
163impl<const N: usize> ReadFromBEBits for FixedU8Buf<N> {
164    fn read_from_be_bits<T: Bits>(inp: &mut T) -> Result<Self, Error> {
165        let mut out = Self::new();
166        inp.read_u32_blob_into(&mut out)?;
167        Ok(out)
168    }
169}
170
171impl<const N: usize> AsRef<[u8]> for FixedU8Buf<N> {
172    #[allow(clippy::indexing_slicing)]
173    fn as_ref(&self) -> &[u8] {
174        &self.buf[..self.len]
175    }
176}
177impl<const N: usize> AsMut<[u8]> for FixedU8Buf<N> {
178    fn as_mut(&mut self) -> &mut [u8] {
179        &mut self.buf
180    }
181}
182impl<const N: usize> Default for FixedU8Buf<N> {
183    fn default() -> Self {
184        Self::new()
185    }
186}
187
188impl<const N: usize> core::fmt::Write for FixedU8Buf<N> {
189    fn write_str(&mut self, s: &str) -> core::fmt::Result {
190        self.append(s.as_bytes()).map_err(|_| core::fmt::Error)?;
191        Ok(())
192    }
193}
194
195impl<const N: usize> Buffer<u8> for FixedU8Buf<N> {
196    fn get(&self, index: usize) -> Option<&u8> {
197        if index >= N || index >= self.len {
198            return None;
199        }
200        self.buf.get(index)
201    }
202
203    fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
204        if index >= N || index >= self.len {
205            return None;
206        }
207        self.buf.get_mut(index)
208    }
209
210    fn capacity(&self) -> usize {
211        N
212    }
213
214    fn len(&self) -> usize {
215        self.len
216    }
217
218    fn clear(&mut self) {
219        self.len = 0
220    }
221
222    fn front(&self) -> Option<&u8> {
223        self.get(0)
224    }
225
226    fn front_mut(&mut self) -> Option<&mut u8> {
227        self.get_mut(0)
228    }
229
230    fn back(&self) -> Option<&u8> {
231        if N == 0 || self.len == 0 {
232            return None;
233        }
234        self.get(self.len - 1)
235    }
236
237    fn back_mut(&mut self) -> Option<&mut u8> {
238        if N == 0 || self.len == 0 {
239            return None;
240        }
241        self.get_mut(self.len - 1)
242    }
243
244    fn pop_front(&mut self) -> Option<u8> {
245        if N == 0 || self.len == 0 {
246            return None;
247        }
248        let out = self.buf[0];
249        for idx in 1..self.len {
250            self.buf[idx - 1] = self.buf[idx];
251        }
252        self.len -= 1;
253        Some(out)
254    }
255
256    fn pop_back(&mut self) -> Option<u8> {
257        if N == 0 || self.len == 0 {
258            return None;
259        }
260        let idx = self.len - 1;
261        self.len -= 1;
262        let val = self.buf[idx];
263        self.buf[idx] = 0;
264        Some(val)
265    }
266
267    fn push_front(&mut self, value: u8) -> Result<(), u8> {
268        if N == 0 || self.len == N {
269            return Err(value);
270        }
271        for idx in 0..self.len {
272            self.buf[idx + 1] = self.buf[idx];
273        }
274        self.buf[0] = value;
275        self.len += 1;
276        Ok(())
277    }
278
279    fn push_back(&mut self, value: u8) -> Result<(), u8> {
280        if N == 0 || self.len == N {
281            return Err(value);
282        }
283        self.buf[self.len] = value;
284        self.len += 1;
285        Ok(())
286    }
287}
288
289#[allow(clippy::panic)]
290impl<const N: usize> Index<usize> for FixedU8Buf<N> {
291    type Output = u8;
292
293    fn index(&self, index: usize) -> &Self::Output {
294        assert!(index < self.len, "index {index} >= len {}", self.len);
295        let Some(val) = self.buf.get(index) else {
296            panic!("expected value at offset {index} but was empty!");
297        };
298        val
299    }
300}
301#[allow(clippy::panic)]
302impl<const N: usize> IndexMut<usize> for FixedU8Buf<N> {
303    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
304        assert!(index < N, "index {index} >= capacity {N}");
305        if index >= self.len {
306            self.len = index + 1;
307        }
308        &mut self.buf[index]
309    }
310}
311pub struct FixedU8BufIter<'a, const N: usize> {
312    buf: &'a FixedU8Buf<N>,
313    idx: usize,
314}
315
316impl<'a, const N: usize> Iterator for FixedU8BufIter<'a, N> {
317    type Item = &'a u8;
318    fn next(&mut self) -> Option<Self::Item> {
319        if let Some(val) = self.buf.get(self.idx) {
320            self.idx += 1;
321            return Some(val);
322        }
323        None
324    }
325}
326impl<const N: usize> DoubleEndedIterator for FixedU8BufIter<'_, N> {
327    fn next_back(&mut self) -> Option<Self::Item> {
328        if self.idx >= self.buf.len {
329            return None;
330        }
331        let idx = self.buf.len().saturating_sub(self.idx).saturating_sub(1);
332
333        self.idx += 1;
334        if let Some(val) = self.buf.get(idx) {
335            return Some(val);
336        }
337        None
338    }
339}
340impl<const N: usize> ExactSizeIterator for FixedU8BufIter<'_, N> {
341    fn len(&self) -> usize {
342        self.buf.len()
343    }
344}
345
346pub struct FixedU8BufIterMut<'a, const N: usize> {
347    buf: &'a mut FixedU8Buf<N>,
348    idx: usize,
349}
350
351impl<const N: usize> LendingIterator for FixedU8BufIterMut<'_, N> {
352    type Item<'b>
353        = &'b mut u8
354    where
355        Self: 'b;
356
357    fn next_ref(&mut self) -> Option<Self::Item<'_>> {
358        if let Some(val) = self.buf.get_mut(self.idx) {
359            self.idx += 1;
360            return Some(val);
361        }
362        None
363    }
364}
365
366impl<const N: usize> MutBits for &mut FixedU8Buf<N> {
367    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
368        if self.push_back(val).is_err() {
369            return Err(BitsErrorKind::UnexpectedEof.into());
370        }
371        Ok(())
372    }
373
374    fn write_all_bytes(&mut self, val: &[u8]) -> Result<(), Error> {
375        self.append(val)
376    }
377}
378impl<const N: usize> MutBits for FixedU8Buf<N> {
379    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
380        if self.push_back(val).is_err() {
381            return Err(BitsErrorKind::UnexpectedEof.into());
382        }
383        Ok(())
384    }
385
386    fn write_all_bytes(&mut self, val: &[u8]) -> Result<(), Error> {
387        self.append(val)
388    }
389}
390
391impl<const N: usize> Bits for FixedU8Buf<N> {
392    fn next_u8(&mut self) -> Result<Option<u8>, Error> {
393        Ok(self.pop_front())
394    }
395}