Skip to main content

rustmod_core/encoding/
reader.rs

1use crate::DecodeError;
2
3/// A zero-copy reader that advances through a byte slice.
4#[derive(Debug, Clone, Copy)]
5pub struct Reader<'a> {
6    buf: &'a [u8],
7    pos: usize,
8}
9
10impl<'a> Reader<'a> {
11    /// Create a reader over the given byte slice.
12    pub const fn new(buf: &'a [u8]) -> Self {
13        Self { buf, pos: 0 }
14    }
15
16    /// Number of bytes consumed so far.
17    pub const fn position(&self) -> usize {
18        self.pos
19    }
20
21    /// Number of bytes remaining in the buffer.
22    pub fn remaining(&self) -> usize {
23        self.buf.len().saturating_sub(self.pos)
24    }
25
26    /// Returns `true` if all bytes have been consumed.
27    pub fn is_empty(&self) -> bool {
28        self.remaining() == 0
29    }
30
31    /// Look at the next byte without advancing the cursor.
32    pub fn peek_u8(&self) -> Result<u8, DecodeError> {
33        self.buf
34            .get(self.pos)
35            .copied()
36            .ok_or(DecodeError::UnexpectedEof)
37    }
38
39    /// Read one byte and advance the cursor.
40    pub fn read_u8(&mut self) -> Result<u8, DecodeError> {
41        let byte = self.peek_u8()?;
42        self.pos += 1;
43        Ok(byte)
44    }
45
46    /// Read exactly `len` bytes as a sub-slice and advance the cursor.
47    pub fn read_exact(&mut self, len: usize) -> Result<&'a [u8], DecodeError> {
48        if self.remaining() < len {
49            return Err(DecodeError::UnexpectedEof);
50        }
51        let start = self.pos;
52        self.pos += len;
53        Ok(&self.buf[start..start + len])
54    }
55
56    /// Read a big-endian `u16` and advance the cursor by 2 bytes.
57    pub fn read_be_u16(&mut self) -> Result<u16, DecodeError> {
58        let bytes = self.read_exact(2)?;
59        Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::Reader;
66    use crate::DecodeError;
67
68    #[test]
69    fn reader_reads_values() {
70        let mut r = Reader::new(&[1, 2, 3, 4]);
71        assert_eq!(r.read_u8().unwrap(), 1);
72        assert_eq!(r.read_exact(2).unwrap(), &[2, 3]);
73        assert_eq!(r.read_be_u16().unwrap_err(), DecodeError::UnexpectedEof);
74    }
75
76    #[test]
77    fn reader_position_and_remaining() {
78        let mut r = Reader::new(&[0x12, 0x34, 0x56]);
79        assert_eq!(r.position(), 0);
80        assert_eq!(r.remaining(), 3);
81        assert_eq!(r.peek_u8().unwrap(), 0x12);
82        assert_eq!(r.read_u8().unwrap(), 0x12);
83        assert_eq!(r.position(), 1);
84        assert_eq!(r.read_be_u16().unwrap(), 0x3456);
85        assert!(r.is_empty());
86    }
87}