srcdmp 0.1.0-alpha.1

Amazing code snapshot tool and library
Documentation
/// Writes variable-length bit sequences into a byte buffer.
///
/// Bits are packed MSB-first into bytes. The caller writes arbitrary
/// numbers of bits via [`write_bits`](BitWriter::write_bits) and then
/// calls [`finish`](BitWriter::finish) to retrieve the final byte vector.
///
/// # Examples
///
/// ```rust
/// use srcdmp::raw::codec::bits::BitWriter;
///
/// let mut w = BitWriter::new();
/// w.write_bits(0b1010, 4);
/// w.write_bits(0b1100, 4);
/// let result = w.finish();
/// assert_eq!(result, vec![0b1010_1100]);
/// ```
pub struct BitWriter {
    buffer: Vec<u8>,
    current: u8,
    bits_used: u8,
}

impl Default for BitWriter {
    fn default() -> Self {
        Self::new()
    }
}

impl BitWriter {
    /// Create a new `BitWriter` with an empty internal buffer.
    #[must_use]
    pub const fn new() -> Self {
        Self {
            buffer: Vec::new(),
            current: 0,
            bits_used: 0,
        }
    }

    /// Write `length` bits from `code`, most significant bit first.
    ///
    /// Bits are extracted starting from the MSB of `code`. `length`
    /// must not exceed the number of valid bits in `code` (typically
    /// 64 or fewer for a `u64` argument).
    pub fn write_bits(&mut self, code: u64, length: u8) {
        for i in (0..length).rev() {
            let bit = ((code >> i) & 1) as u8;
            self.current = (self.current << 1) | bit;
            self.bits_used += 1;

            if self.bits_used == 8 {
                self.buffer.push(self.current);
                self.current = 0;
                self.bits_used = 0;
            }
        }
    }

    /// Flush remaining bits (padded with zeros) and return the byte buffer.
    #[must_use]
    pub fn finish(mut self) -> Vec<u8> {
        if self.bits_used > 0 {
            self.current <<= 8 - self.bits_used;
            self.buffer.push(self.current);
        }
        self.buffer
    }

    /// Return the number of zero bits that will be appended during [`finish`](Self::finish).
    #[must_use]
    pub const fn padding_bits(&self) -> u8 {
        if self.bits_used == 0 {
            0
        } else {
            8 - self.bits_used
        }
    }
}

/// Reads variable-length bit sequences from a byte buffer.
///
/// Bits are read MSB-first from the buffer. The reader must be
/// configured with the number of padding bits that were added
/// during encoding to correctly detect the end of the stream.
///
/// # Examples
///
/// ```rust
/// use srcdmp::raw::codec::bits::BitReader;
///
/// let data = vec![0b1010_1100];
/// let mut r = BitReader::new(&data, 0);
/// assert_eq!(r.read_bit(), Some(1));
/// assert_eq!(r.read_bit(), Some(0));
/// assert_eq!(r.read_bit(), Some(1));
/// assert_eq!(r.read_bit(), Some(0));
/// assert_eq!(r.read_bit(), Some(1));
/// assert_eq!(r.read_bit(), Some(1));
/// assert_eq!(r.read_bit(), Some(0));
/// assert_eq!(r.read_bit(), Some(0));
/// assert_eq!(r.read_bit(), None);
/// ```
pub struct BitReader<'a> {
    data: &'a [u8],
    byte_pos: usize,
    bit_pos: u8,
    padding: u8,
}

impl<'a> BitReader<'a> {
    /// Create a new `BitReader` over the given byte slice.
    ///
    /// `padding` is the number of trailing zero bits that were added
    /// during encoding to align to a byte boundary. The reader will
    /// stop before reading those padding bits.
    #[must_use]
    pub const fn new(data: &'a [u8], padding: u8) -> Self {
        Self {
            data,
            byte_pos: 0,
            bit_pos: 0,
            padding,
        }
    }

    /// Read a single bit, returning `None` when the stream is exhausted.
    pub fn read_bit(&mut self) -> Option<u8> {
        let is_last_byte = self.byte_pos == self.data.len() - 1;
        if is_last_byte && self.bit_pos >= (8 - self.padding) {
            return None;
        }
        if self.byte_pos >= self.data.len() {
            return None;
        }

        let bit = (self.data[self.byte_pos] >> (7 - self.bit_pos)) & 1;
        self.bit_pos += 1;

        if self.bit_pos == 8 {
            self.bit_pos = 0;
            self.byte_pos += 1;
        }

        Some(bit)
    }
}