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        for b in buf {
112            let _ = self.push_back(*b);
113        }
114        Ok(())
115    }
116
117    ///
118    /// Returns the contents of this buffer as a str
119    pub fn as_str(&self) -> Result<&str, Utf8Error> {
120        core::str::from_utf8(self.as_ref_used())
121    }
122    ///
123    /// Returns the contents of this buffer as a mutable str
124    pub fn as_str_mut(&mut self) -> Result<&mut str, Utf8Error> {
125        core::str::from_utf8_mut(self.as_mut_used())
126    }
127
128    ///
129    /// Basic in-place swap.
130    pub fn reverse(&mut self) {
131        let mut i = 0;
132        let mut j = self.len - 1;
133        while i < j {
134            self.buf.swap(i, j);
135            i += 1;
136            j -= 1;
137        }
138    }
139}
140impl<const N: usize> WriteToBEBits for FixedU8Buf<N> {
141    fn write_be_to<T: MutBits + ?Sized>(&self, bits: &mut T) -> Result<usize, Error> {
142        bits.write_be_u32_blob(self.as_ref_used())?;
143        Ok(self.len + 4)
144    }
145}
146impl<const N: usize> ReadFromBEBits for FixedU8Buf<N> {
147    fn read_from_be_bits<T: Bits>(inp: &mut T) -> Result<Self, Error> {
148        let mut out = Self::new();
149        inp.read_u32_blob_into(&mut out)?;
150        Ok(out)
151    }
152}
153
154impl<const N: usize> AsRef<[u8]> for FixedU8Buf<N> {
155    #[allow(clippy::indexing_slicing)]
156    fn as_ref(&self) -> &[u8] {
157        &self.buf[..self.len]
158    }
159}
160impl<const N: usize> AsMut<[u8]> for FixedU8Buf<N> {
161    fn as_mut(&mut self) -> &mut [u8] {
162        &mut self.buf
163    }
164}
165impl<const N: usize> Default for FixedU8Buf<N> {
166    fn default() -> Self {
167        Self::new()
168    }
169}
170
171impl<const N: usize> core::fmt::Write for FixedU8Buf<N> {
172    fn write_str(&mut self, s: &str) -> core::fmt::Result {
173        self.append(s.as_bytes()).map_err(|_| core::fmt::Error)?;
174        Ok(())
175    }
176}
177
178impl<const N: usize> Buffer<u8> for FixedU8Buf<N> {
179    fn get(&self, index: usize) -> Option<&u8> {
180        if index >= N || index >= self.len {
181            return None;
182        }
183        self.buf.get(index)
184    }
185
186    fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
187        if index >= N || index >= self.len {
188            return None;
189        }
190        self.buf.get_mut(index)
191    }
192
193    fn capacity(&self) -> usize {
194        N
195    }
196
197    fn len(&self) -> usize {
198        self.len
199    }
200
201    fn clear(&mut self) {
202        self.len = 0
203    }
204
205    fn front(&self) -> Option<&u8> {
206        self.get(0)
207    }
208
209    fn front_mut(&mut self) -> Option<&mut u8> {
210        self.get_mut(0)
211    }
212
213    fn back(&self) -> Option<&u8> {
214        if N == 0 || self.len == 0 {
215            return None;
216        }
217        self.get(self.len - 1)
218    }
219
220    fn back_mut(&mut self) -> Option<&mut u8> {
221        if N == 0 || self.len == 0 {
222            return None;
223        }
224        self.get_mut(self.len - 1)
225    }
226
227    fn pop_front(&mut self) -> Option<u8> {
228        if N == 0 || self.len == 0 {
229            return None;
230        }
231        let out = self.buf[0];
232        for idx in 1..self.len {
233            self.buf[idx - 1] = self.buf[idx];
234        }
235        self.len -= 1;
236        Some(out)
237    }
238
239    fn pop_back(&mut self) -> Option<u8> {
240        if N == 0 || self.len == 0 {
241            return None;
242        }
243        let idx = self.len - 1;
244        self.len -= 1;
245        let val = self.buf[idx];
246        self.buf[idx] = 0;
247        Some(val)
248    }
249
250    fn push_front(&mut self, value: u8) -> Result<(), u8> {
251        if N == 0 || self.len == N {
252            return Err(value);
253        }
254        for idx in 0..self.len {
255            self.buf[idx + 1] = self.buf[idx];
256        }
257        self.buf[0] = value;
258        self.len += 1;
259        Ok(())
260    }
261
262    fn push_back(&mut self, value: u8) -> Result<(), u8> {
263        if N == 0 || self.len == N {
264            return Err(value);
265        }
266        self.buf[self.len] = value;
267        self.len += 1;
268        Ok(())
269    }
270}
271
272#[allow(clippy::panic)]
273impl<const N: usize> Index<usize> for FixedU8Buf<N> {
274    type Output = u8;
275
276    fn index(&self, index: usize) -> &Self::Output {
277        assert!(index < self.len, "index {index} >= len {}", self.len);
278        let Some(val) = self.buf.get(index) else {
279            panic!("expected value at offset {index} but was empty!");
280        };
281        val
282    }
283}
284#[allow(clippy::panic)]
285impl<const N: usize> IndexMut<usize> for FixedU8Buf<N> {
286    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
287        assert!(index < N, "index {index} >= capacity {N}");
288        if index >= self.len {
289            self.len = index + 1;
290        }
291        &mut self.buf[index]
292    }
293}
294
295pub struct FixedU8BufIter<'a, const N: usize> {
296    buf: &'a FixedU8Buf<N>,
297    idx: usize,
298}
299
300impl<'a, const N: usize> Iterator for FixedU8BufIter<'a, N> {
301    type Item = &'a u8;
302    fn next(&mut self) -> Option<Self::Item> {
303        if let Some(val) = self.buf.get(self.idx) {
304            self.idx += 1;
305            return Some(val);
306        }
307        None
308    }
309}
310impl<const N: usize> DoubleEndedIterator for FixedU8BufIter<'_, N> {
311    fn next_back(&mut self) -> Option<Self::Item> {
312        if self.idx >= self.buf.len {
313            return None;
314        }
315        let idx = self.buf.len().saturating_sub(self.idx).saturating_sub(1);
316
317        self.idx += 1;
318        if let Some(val) = self.buf.get(idx) {
319            return Some(val);
320        }
321        None
322    }
323}
324impl<const N: usize> ExactSizeIterator for FixedU8BufIter<'_, N> {
325    fn len(&self) -> usize {
326        self.buf.len()
327    }
328}
329
330pub struct FixedU8BufIterMut<'a, const N: usize> {
331    buf: &'a mut FixedU8Buf<N>,
332    idx: usize,
333}
334
335impl<const N: usize> LendingIterator for FixedU8BufIterMut<'_, N> {
336    type Item<'b>
337        = &'b mut u8
338    where
339        Self: 'b;
340
341    fn next_ref(&mut self) -> Option<Self::Item<'_>> {
342        if let Some(val) = self.buf.get_mut(self.idx) {
343            self.idx += 1;
344            return Some(val);
345        }
346        None
347    }
348}
349
350impl<const N: usize> MutBits for &mut FixedU8Buf<N> {
351    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
352        if self.push_back(val).is_err() {
353            return Err(BitsErrorKind::UnexpectedEof.into());
354        }
355        Ok(())
356    }
357}
358impl<const N: usize> MutBits for FixedU8Buf<N> {
359    fn write_u8(&mut self, val: u8) -> Result<(), Error> {
360        if self.push_back(val).is_err() {
361            return Err(BitsErrorKind::UnexpectedEof.into());
362        }
363        Ok(())
364    }
365}