bitfield-serialize 0.1.0

A Rust library for defining and serializing bitfield structures with macro support
Documentation
//! Utility structures and functions for bit manipulation.

/// A utility for writing bits to a byte vector.
#[derive(Debug, Clone)]
pub struct BitWriter {
    data: Vec<u8>,
    bit_pos: usize,
}

impl BitWriter {
    /// Create a new BitWriter.
    pub fn new() -> Self {
        Self {
            data: Vec::new(),
            bit_pos: 0,
        }
    }

    /// Write multiple bits from a u64 value.
    pub fn write_bits(&mut self, value: u64, num_bits: usize) {
        for i in 0..num_bits {
            // Extract bits from MSB first
            let bit = (value >> (num_bits - 1 - i)) & 1;
            self.write_bit(bit != 0);
        }
    }

    /// Write a single bit.
    pub fn write_bit(&mut self, bit: bool) {
        let byte_pos = self.bit_pos / 8;
        // Use big-endian bit order within each byte
        let bit_offset = 7 - (self.bit_pos % 8);

        // Ensure we have enough bytes
        while self.data.len() <= byte_pos {
            self.data.push(0);
        }

        if bit {
            self.data[byte_pos] |= 1 << bit_offset;
        }

        self.bit_pos += 1;
    }

    /// Write bits directly to an existing byte array.
    pub fn write_bits_to(data: &mut [u8], start_bit: usize, value: u64, num_bits: usize) {
        for i in 0..num_bits {
            let bit_pos = start_bit + i;
            let byte_pos = bit_pos / 8;
            // Big-endian bit order in byte
            let bit_offset = 7 - (bit_pos % 8);

            if byte_pos >= data.len() {
                break;
            }

            // Extract bits MSB first
            let bit = (value >> (num_bits - 1 - i)) & 1;
            if bit != 0 {
                data[byte_pos] |= 1 << bit_offset;
            } else {
                data[byte_pos] &= !(1 << bit_offset);
            }
        }
    }

    /// Finish writing and return the byte vector.
    pub fn finish(mut self) -> Vec<u8> {
        // Ensure the last byte is included if we wrote any bits to it
        if self.bit_pos % 8 != 0 {
            let byte_pos = self.bit_pos / 8;
            while self.data.len() <= byte_pos {
                self.data.push(0);
            }
        }
        self.data
    }

    /// Get the current bit position.
    pub fn bit_position(&self) -> usize {
        self.bit_pos
    }

    /// Get a reference to the current data.
    pub fn data(&self) -> &[u8] {
        &self.data
    }
}

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

/// A utility for reading bits from a byte slice.
#[derive(Debug, Clone)]
pub struct BitReader<'a> {
    data: &'a [u8],
    bit_pos: usize,
}

impl<'a> BitReader<'a> {
    /// Create a new BitReader.
    pub fn new(data: &'a [u8]) -> Self {
        Self { data, bit_pos: 0 }
    }

    /// Read multiple bits as a u64 value.
    pub fn read_bits(&mut self, num_bits: usize) -> Result<u64, String> {
        let mut value = 0u64;
        for i in 0..num_bits {
            let bit = self.read_bit()?;
            if bit {
                // Assemble value MSB first
                value |= 1 << (num_bits - 1 - i);
            }
        }
        Ok(value)
    }

    /// Read a single bit.
    pub fn read_bit(&mut self) -> Result<bool, String> {
        let byte_pos = self.bit_pos / 8;
        // Big-endian bit order
        let bit_offset = 7 - (self.bit_pos % 8);

        if byte_pos >= self.data.len() {
            return Err("Unexpected end of data".to_string());
        }

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

        Ok(bit != 0)
    }

    /// Read bits from a specific position without advancing the reader.
    pub fn read_bits_at(data: &[u8], start_bit: usize, num_bits: usize) -> u64 {
        let mut value = 0u64;
        for i in 0..num_bits {
            let bit_pos = start_bit + i;
            let byte_pos = bit_pos / 8;
            // Big-endian bit order
            let bit_offset = 7 - (bit_pos % 8);

            if byte_pos >= data.len() {
                break;
            }

            let bit = (data[byte_pos] >> bit_offset) & 1;
            if bit != 0 {
                // Assemble MSB first
                value |= 1 << (num_bits - 1 - i);
            }
        }
        value
    }

    /// Get the current bit position.
    pub fn bit_position(&self) -> usize {
        self.bit_pos
    }

    /// Check if there are more bits to read.
    pub fn has_bits(&self) -> bool {
        self.bit_pos < self.data.len() * 8
    }

    /// Reset the reader to the beginning.
    pub fn reset(&mut self) {
        self.bit_pos = 0;
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bit_writer_basic() {
        let mut writer = BitWriter::new();
        writer.write_bits(0b1010, 4);
        let data = writer.finish();
        // Should write in big-endian into high bits of byte
        assert_eq!(data, vec![0b1010_0000]);
    }

    #[test]
    fn test_bit_writer_multiple_bytes() {
        let mut writer = BitWriter::new();
        writer.write_bits(0xFF, 8); // First byte
        writer.write_bits(0xAA, 8); // Second byte
        let data = writer.finish();
        assert_eq!(data, vec![0xFF, 0xAA]);
    }

    #[test]
    fn test_bit_reader_basic() {
        // Data in big-endian nibble
        let data = vec![0b1010_0000];
        let mut reader = BitReader::new(&data);
        let value = reader.read_bits(4).unwrap();
        assert_eq!(value, 0b1010);
    }

    #[test]
    fn test_bit_reader_multiple_bytes() {
        let data = vec![0xFF, 0xAA];
        let mut reader = BitReader::new(&data);
        let first = reader.read_bits(8).unwrap();
        let second = reader.read_bits(8).unwrap();
        assert_eq!(first, 0xFF);
        assert_eq!(second, 0xAA);
    }

    #[test]
    fn test_roundtrip() {
        let mut writer = BitWriter::new();
        writer.write_bits(0b10101010, 8);
        writer.write_bits(0b11001100, 8);
        let data = writer.finish();

        assert_eq!(data, vec![0b10101010, 0b11001100]);

        let mut reader = BitReader::new(&data);
        let first = reader.read_bits(8).unwrap();
        let second = reader.read_bits(8).unwrap();

        assert_eq!(first, 0b10101010);
        assert_eq!(second, 0b11001100);
    }

    #[test]
    fn test_write_bits_to() {
        let mut data = vec![0u8; 2];
        BitWriter::write_bits_to(&mut data, 0, 0b1010, 4);
        BitWriter::write_bits_to(&mut data, 4, 0b1100, 4);
        // High nibble 1010, low nibble 1100
        assert_eq!(data[0], 0b1010_1100);
    }

    #[test]
    fn test_read_bits_at() {
        let data = vec![0b1010_1100];
        let first_nibble = BitReader::read_bits_at(&data, 0, 4);
        let second_nibble = BitReader::read_bits_at(&data, 4, 4);
        assert_eq!(first_nibble, 0b1010);
        assert_eq!(second_nibble, 0b1100);
    }
}