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    pub const fn new(buf: &'a [u8]) -> Self {
12        Self { buf, pos: 0 }
13    }
14
15    pub const fn position(&self) -> usize {
16        self.pos
17    }
18
19    pub fn remaining(&self) -> usize {
20        self.buf.len().saturating_sub(self.pos)
21    }
22
23    pub fn is_empty(&self) -> bool {
24        self.remaining() == 0
25    }
26
27    pub fn peek_u8(&self) -> Result<u8, DecodeError> {
28        self.buf
29            .get(self.pos)
30            .copied()
31            .ok_or(DecodeError::UnexpectedEof)
32    }
33
34    pub fn read_u8(&mut self) -> Result<u8, DecodeError> {
35        let byte = self.peek_u8()?;
36        self.pos += 1;
37        Ok(byte)
38    }
39
40    pub fn read_exact(&mut self, len: usize) -> Result<&'a [u8], DecodeError> {
41        if self.remaining() < len {
42            return Err(DecodeError::UnexpectedEof);
43        }
44        let start = self.pos;
45        self.pos += len;
46        Ok(&self.buf[start..start + len])
47    }
48
49    pub fn read_be_u16(&mut self) -> Result<u16, DecodeError> {
50        let bytes = self.read_exact(2)?;
51        Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::Reader;
58    use crate::DecodeError;
59
60    #[test]
61    fn reader_reads_values() {
62        let mut r = Reader::new(&[1, 2, 3, 4]);
63        assert_eq!(r.read_u8().unwrap(), 1);
64        assert_eq!(r.read_exact(2).unwrap(), &[2, 3]);
65        assert_eq!(r.read_be_u16().unwrap_err(), DecodeError::UnexpectedEof);
66    }
67
68    #[test]
69    fn reader_position_and_remaining() {
70        let mut r = Reader::new(&[0x12, 0x34, 0x56]);
71        assert_eq!(r.position(), 0);
72        assert_eq!(r.remaining(), 3);
73        assert_eq!(r.peek_u8().unwrap(), 0x12);
74        assert_eq!(r.read_u8().unwrap(), 0x12);
75        assert_eq!(r.position(), 1);
76        assert_eq!(r.read_be_u16().unwrap(), 0x3456);
77        assert!(r.is_empty());
78    }
79}