blackbox_log/
reader.rs

1use core::fmt;
2
3/// A wrapper around a byte slice to efficiently read data from a blackbox log.
4#[derive(Clone)]
5pub(crate) struct Reader<'data> {
6    /// Index of the next byte to read
7    index: usize,
8    data: &'data [u8],
9}
10
11/// Opaque type used to rewind a `Reader`.
12#[derive(Debug, Clone)]
13pub(crate) struct RestorePoint(usize);
14
15impl<'data> Reader<'data> {
16    /// Creates a new `Reader` starting at the beginning of `data`.
17    ///
18    /// # Panics
19    ///
20    /// This will panic if `data` has a length of `usize::MAX`, since `Reader`
21    /// relies on being able to internally store an index `>= data.len()`.
22    #[inline]
23    #[must_use]
24    pub(crate) const fn new(data: &'data [u8]) -> Self {
25        if data.len() == usize::MAX {
26            panic!("cannot create a Reader containing usize::MAX bytes");
27        }
28
29        Self { index: 0, data }
30    }
31
32    /// Returns a value that can be passed to [`Reader::restore`] to rewind to
33    /// the current index.
34    pub(crate) const fn get_restore_point(&self) -> RestorePoint {
35        RestorePoint(self.index)
36    }
37
38    /// Rewinds to a stored [`RestorePoint`] from [`Reader::get_restore_point`].
39    pub(crate) fn restore(&mut self, restore: RestorePoint) {
40        self.index = restore.0;
41    }
42
43    /// Advances past all bytes not matching any of the needles, returning
44    /// `true` if any are found before the end of the buffer.
45    pub(crate) fn skip_until_any(&mut self, needles: &[u8]) -> bool {
46        debug_assert_ne!(
47            needles.len(),
48            0,
49            "searching for any of 0 bytes makes no sense"
50        );
51
52        let position = self.data[self.index..]
53            .iter()
54            .position(|x| needles.contains(x));
55
56        if let Some(position) = position {
57            self.index += position;
58        }
59
60        position.is_some()
61    }
62
63    /// Returns the number of bytes that have not yet been read.
64    #[must_use]
65    pub(crate) const fn remaining(&self) -> usize {
66        self.data.len() - self.index
67    }
68
69    /// Returns true if the [`Reader`] has reached the end of the underlying
70    /// buffer.
71    #[must_use]
72    #[cfg_attr(not(test), expect(dead_code))]
73    pub(crate) fn is_empty(&self) -> bool {
74        self.remaining() == 0
75    }
76
77    /// Returns the next byte without advancing.
78    pub(crate) fn peek(&self) -> Option<u8> {
79        self.data.get(self.index).copied()
80    }
81
82    /// Reads all bytes up to, but not including, the next newline, or the end
83    /// of the buffer.
84    ///
85    /// This is roughly equivalent to `data.iter().take_while(|x| x != b'\n')`,
86    /// but is more concise and may be faster.
87    pub(crate) fn read_line(&mut self) -> Option<&'data [u8]> {
88        let start = self.index;
89
90        let rest = self.data.get(start..).filter(|x| !x.is_empty())?;
91
92        if let Some(len) = rest.iter().position(|b| *b == b'\n') {
93            self.index += len + 1; // Skip the '\n'
94
95            let end = start + len;
96            self.data.get(start..end)
97        } else {
98            self.index = self.data.len();
99            self.data.get(start..)
100        }
101    }
102
103    /// Attempts to read the next `n` bytes, returning the rest of the buffer if
104    /// there are fewer than `n` remaining.
105    pub(crate) fn read_n_bytes(&mut self, n: usize) -> &'data [u8] {
106        let len = n.min(self.remaining());
107
108        let start = self.index;
109        let slice = &self.data[start..(start + len)];
110
111        self.index += len;
112        slice
113    }
114
115    /// Reads a single byte as a `u8`.
116    pub(crate) fn read_u8(&mut self) -> Option<u8> {
117        let byte = self.peek();
118        if byte.is_some() {
119            self.index += 1;
120        }
121        byte
122    }
123
124    /// Reads a single byte as an `i8`.
125    pub(crate) fn read_i8(&mut self) -> Option<i8> {
126        self.read_u8().map(u8::cast_signed)
127    }
128
129    /// Reads 3 bytes as the lower bytes of a `u32`.
130    pub(crate) fn read_u24(&mut self) -> Option<u32> {
131        if self.remaining() < 3 {
132            return None;
133        }
134
135        let mut bytes = [0; 4];
136        let slice = &mut bytes[0..3];
137
138        let start = self.index;
139        slice.copy_from_slice(&self.data[start..(start + 3)]);
140        self.index += 3;
141
142        Some(u32::from_le_bytes(bytes))
143    }
144
145    pub(crate) fn read_f32(&mut self) -> Option<f32> {
146        self.read_u32().map(f32::from_bits)
147    }
148}
149
150macro_rules! impl_read {
151    ($read:ident, $type:ty, $iread:ident, $itype:ty) => {
152        #[allow(dead_code)]
153        pub(crate) fn $read(&mut self) -> Option<$type> {
154            const BYTES: usize = (<$type>::BITS / 8) as usize;
155
156            if self.remaining() < BYTES {
157                return None;
158            }
159
160            let mut bytes = [0; BYTES];
161            let start = self.index;
162            bytes.copy_from_slice(&self.data[start..(start + BYTES)]);
163            self.index += BYTES;
164
165            Some(<$type>::from_le_bytes(bytes))
166        }
167
168        #[allow(dead_code)]
169        pub(crate) fn $iread(&mut self) -> Option<$itype> {
170            #[expect(clippy::cast_possible_wrap)]
171            self.$read().map(|x| x as $itype)
172        }
173    };
174}
175
176impl Reader<'_> {
177    impl_read!(read_u16, u16, read_i16, i16);
178
179    impl_read!(read_u32, u32, read_i32, i32);
180
181    impl_read!(read_u64, u64, read_i64, i64);
182
183    impl_read!(read_u128, u128, read_i128, i128);
184}
185
186impl fmt::Debug for Reader<'_> {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        f.debug_struct("Reader")
189            .field("index", &self.index)
190            .finish_non_exhaustive()
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::*;
197
198    #[test]
199    fn restore() {
200        let mut bytes = Reader::new(&[0, 1, 2]);
201        bytes.read_u8();
202        let restore = bytes.get_restore_point();
203        bytes.read_u16();
204        assert!(bytes.is_empty());
205        bytes.restore(restore);
206        assert_eq!(Some(1), bytes.read_u8());
207    }
208
209    #[test]
210    fn skip_until_any() {
211        let mut bytes = Reader::new(&[10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
212        bytes.read_u8();
213        assert!(bytes.skip_until_any(&[10, 9]));
214        assert_eq!(Some(9), bytes.read_u8());
215    }
216
217    #[test]
218    fn skip_until_any_not_found() {
219        let mut bytes = Reader::new(&[2, 3, 4]);
220        assert!(!bytes.skip_until_any(&[0, 1]));
221    }
222
223    #[test]
224    fn skip_until_any_no_skip() {
225        let mut bytes = Reader::new(&[0]);
226        assert!(bytes.skip_until_any(&[0]));
227        assert_eq!(Some(0), bytes.read_u8());
228        assert!(bytes.is_empty());
229    }
230
231    #[test]
232    fn read_u16() {
233        let mut bytes = Reader::new(&[0x39, 0x05]);
234        assert_eq!(Some(0x0539), bytes.read_u16());
235    }
236
237    #[test]
238    fn read_i16() {
239        let mut bytes = Reader::new(&[0xC7, 0xFA]);
240        assert_eq!(Some(-0x0539), bytes.read_i16());
241    }
242
243    #[test]
244    fn read_u24() {
245        let mut bytes = Reader::new(&[0x56, 0x34, 0x12]);
246        assert_eq!(Some(0x123456), bytes.read_u24());
247    }
248
249    #[test]
250    fn read_u32() {
251        let mut bytes = Reader::new(&[0xEF, 0xCD, 0x34, 0x12]);
252        assert_eq!(Some(0x1234_CDEF), bytes.read_u32());
253    }
254
255    #[test]
256    fn read_i32() {
257        let mut bytes = Reader::new(&[0x11, 0x32, 0xCB, 0xED]);
258        assert_eq!(Some(-0x1234_CDEF), bytes.read_i32());
259    }
260
261    #[test]
262    fn read_u64() {
263        let mut bytes = Reader::new(&[0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01]);
264        assert_eq!(Some(0x0123_4567_89AB_CDEF), bytes.read_u64());
265    }
266
267    #[test]
268    fn read_i64() {
269        let mut bytes = Reader::new(&[1, 0, 0, 0, 0, 0, 0, 0x80]);
270        assert_eq!(Some(i64::MIN + 1), bytes.read_i64());
271    }
272
273    #[test]
274    fn read_u128() {
275        let mut bytes = Reader::new(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF]);
276        assert_eq!(
277            Some(0x0F0E_0D0C_0B0A_0908_0706_0504_0302_0100),
278            bytes.read_u128()
279        );
280    }
281
282    #[test]
283    fn read_i128() {
284        let mut bytes = Reader::new(&[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80]);
285        assert_eq!(Some(i128::MIN + 1), bytes.read_i128());
286    }
287
288    #[test]
289    fn bytes_read_line() {
290        let mut bytes = Reader::new(&[b'a', 0, b'\n', b'b']);
291
292        assert_eq!(Some(b"a\0".as_ref()), bytes.read_line());
293        assert_eq!(Some(b'b'), bytes.read_u8());
294    }
295
296    #[test]
297    fn bytes_read_n_bytes_exact() {
298        let input = [0, 1, 2, 3];
299
300        let mut bytes = Reader::new(&input);
301
302        let read = bytes.read_n_bytes(1);
303        assert_eq!(read.len(), 1);
304        assert_eq!(read, &input[0..1]);
305
306        let read = bytes.read_n_bytes(0);
307        assert_eq!(read.len(), 0);
308
309        let read = bytes.read_n_bytes(3);
310        assert_eq!(read.len(), 3);
311        assert_eq!(read, &input[1..]);
312
313        assert!(bytes.is_empty());
314    }
315
316    #[test]
317    fn bytes_read_n_bytes_overshoot() {
318        let input = [0];
319
320        let mut bytes = Reader::new(&input);
321
322        let read = bytes.read_n_bytes(2);
323        assert_eq!(read.len(), 1);
324        assert_eq!(read, input);
325
326        assert!(bytes.is_empty());
327    }
328
329    #[test]
330    fn bytes_read_line_without_newline() {
331        let mut bytes = Reader::new(&[b'a', 0]);
332
333        assert_eq!(Some(b"a\0".as_ref()), bytes.read_line());
334        assert_eq!(None, bytes.read_u8());
335    }
336
337    #[test]
338    fn bytes_read_line_empty() {
339        let mut bytes = Reader::new(&[]);
340        assert_eq!(None, bytes.read_line());
341    }
342}