lc3_codec/decoder/
buffer_reader.rs

1use byteorder::{BigEndian, ByteOrder};
2
3#[derive(Debug)]
4pub enum BufferReaderError {
5    ReadByteOutOfBounds(usize),
6    ReadU24OutOfBounds(usize),
7    BigEndianBitReaderReadUsizeNumBitsOutOfRange(usize, usize),
8    BigEndianBitReaderReadBoolOutOfRange(usize),
9}
10
11#[derive(Default)]
12pub struct BufferReader {
13    head_byte_cursor: usize,
14    tail_bit_cursor: usize,
15}
16
17// Big Endian buffer reader
18// This reader can read bits from the tail end of the buffer
19// (working its way towards the head) as well as reading bytes from the head of
20// the buffer working its way to the tail.
21// FIXME: return error if head cursor meets tail cursor
22impl BufferReader {
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    pub fn new_at(head_byte_cursor: usize, tail_bit_cursor: usize) -> Self {
28        Self {
29            head_byte_cursor,
30            tail_bit_cursor,
31        }
32    }
33
34    pub fn get_tail_bit_cursor(&self) -> usize {
35        self.tail_bit_cursor
36    }
37
38    pub fn get_head_byte_cursor(&self) -> usize {
39        self.head_byte_cursor
40    }
41
42    pub fn read_head_byte(&mut self, buf: &[u8]) -> Result<u8, BufferReaderError> {
43        match buf.get(self.head_byte_cursor) {
44            Some(byte) => {
45                self.head_byte_cursor += 1;
46                Ok(*byte)
47            }
48            None => Err(BufferReaderError::ReadByteOutOfBounds(self.head_byte_cursor)),
49        }
50    }
51
52    pub fn read_head_u24(&mut self, buf: &[u8]) -> Result<u32, BufferReaderError> {
53        if self.head_byte_cursor + 2 < buf.len() {
54            let value = BigEndian::read_u24(&buf[self.head_byte_cursor..]);
55            self.head_byte_cursor += 3;
56            Ok(value)
57        } else {
58            Err(BufferReaderError::ReadU24OutOfBounds(self.head_byte_cursor))
59        }
60    }
61
62    // This function will read bits from the end of a buffer towards the front and interpret multi-byte payloads in BigEndian format
63    pub fn read_tail_usize(&mut self, buf: &[u8], num_bits: usize) -> Result<usize, BufferReaderError> {
64        let byte_index = self.tail_bit_cursor / 8;
65        let bit_index = self.tail_bit_cursor % 8;
66
67        let bits_left = 8 - bit_index;
68        let add_bytes = if num_bits > bits_left && num_bits < 8 { 2 } else { 1 };
69        let num_bytes = num_bits / 8 + add_bytes;
70
71        if (buf.len() as i32 - self.head_byte_cursor as i32 - byte_index as i32 - num_bytes as i32) < 0 {
72            return Err(BufferReaderError::BigEndianBitReaderReadUsizeNumBitsOutOfRange(
73                num_bits, bit_index,
74            ));
75        }
76
77        let from_index = buf.len() - byte_index - num_bytes;
78        let to_index = from_index + num_bytes;
79        let slice = &buf[from_index..to_index];
80
81        let mut value = match num_bytes {
82            1 => slice[0] as u32,
83            2 => BigEndian::read_u16(slice) as u32,
84            3 => BigEndian::read_u24(slice) as u32,
85            4 => BigEndian::read_u32(slice),
86            _ => 0_u32,
87        };
88
89        // shift the bits we want to ignore out of the way
90        let shift_by = 32 - num_bits - bit_index;
91        value <<= shift_by;
92        value >>= shift_by + bit_index;
93
94        self.tail_bit_cursor += num_bits;
95        Ok(value as usize)
96    }
97
98    pub fn read_tail_bool(&mut self, buf: &[u8]) -> Result<bool, BufferReaderError> {
99        let byte_index = self.tail_bit_cursor / 8;
100        let bit_index = self.tail_bit_cursor % 8;
101
102        // FIXME: test this range check properly
103        if (buf.len() as i32 - self.head_byte_cursor as i32 - byte_index as i32 + 2) < 0 {
104            return Err(BufferReaderError::BigEndianBitReaderReadBoolOutOfRange(bit_index));
105        }
106
107        let from_index = buf.len() - byte_index - 1;
108        let mut byte = buf[from_index];
109        byte <<= 7 - bit_index;
110        byte >>= 7;
111
112        self.tail_bit_cursor += 1;
113        Ok(byte == 1)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    extern crate std;
120    use super::*;
121
122    #[test]
123    fn read_5_bits_over_byte_boundary_unto_usize() {
124        let buf = [248, 52, 26, 166, 60];
125        let mut reader = BufferReader::new();
126        reader.tail_bit_cursor = 23;
127        let value1 = reader.read_tail_usize(&buf, 5).unwrap();
128        assert_eq!(8, value1);
129    }
130
131    #[test]
132    fn read_multiple_values_from_bigendian_bitstream() {
133        // value positions:         222    2222 2111
134        let buf: [u8; 2] = [0b0001_1011, 0b0000_1100];
135        let mut reader = BufferReader::new();
136
137        // 0b0000_0100
138        let value1 = reader.read_tail_usize(&buf, 3).unwrap();
139        // 0b0110_0001
140        let value2 = reader.read_tail_usize(&buf, 8).unwrap();
141
142        assert_eq!(4, value1);
143        assert_eq!(97, value2);
144    }
145
146    #[test]
147    fn read_bool_from_bigendian_bitstream() {
148        let byte = 0b0100_1000;
149        let buf: [u8; 1] = [byte];
150        let mut reader = BufferReader::new();
151
152        let value0 = reader.read_tail_bool(&buf).unwrap();
153        let value1 = reader.read_tail_bool(&buf).unwrap();
154        let value2 = reader.read_tail_bool(&buf).unwrap();
155        let value3 = reader.read_tail_bool(&buf).unwrap();
156        let value4 = reader.read_tail_bool(&buf).unwrap();
157        let value5 = reader.read_tail_bool(&buf).unwrap();
158        let value6 = reader.read_tail_bool(&buf).unwrap();
159        let value7 = reader.read_tail_bool(&buf).unwrap();
160
161        assert_eq!(false, value0);
162        assert_eq!(false, value1);
163        assert_eq!(false, value2);
164        assert_eq!(true, value3);
165        assert_eq!(false, value4);
166        assert_eq!(false, value5);
167        assert_eq!(true, value6);
168        assert_eq!(false, value7);
169    }
170}