#[derive(Debug, Default, Clone)]
pub struct BitWriter {
bytes: Vec<u8>,
bit_pos: u8,
}
impl BitWriter {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn put_bit(&mut self, bit: u8) {
if self.bit_pos == 0 {
self.bytes.push(0);
}
if bit & 1 != 0 {
let last = self.bytes.len() - 1;
self.bytes[last] |= 1 << (7 - self.bit_pos);
}
self.bit_pos = (self.bit_pos + 1) & 7;
}
pub fn put_bits(&mut self, value: u32, n: u32) {
debug_assert!(n <= 32);
for i in (0..n).rev() {
self.put_bit(((value >> i) & 1) as u8);
}
}
#[must_use]
pub fn bit_len(&self) -> usize {
if self.bit_pos == 0 {
self.bytes.len() * 8
} else {
(self.bytes.len() - 1) * 8 + self.bit_pos as usize
}
}
#[must_use]
pub fn is_byte_aligned(&self) -> bool {
self.bit_pos == 0
}
pub fn byte_align(&mut self) {
while self.bit_pos != 0 {
self.put_bit(0);
}
}
pub fn put_bytes(&mut self, data: &[u8]) {
debug_assert!(self.is_byte_aligned(), "put_bytes requires byte alignment");
self.bytes.extend_from_slice(data);
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
}
#[cfg(test)]
mod tests {
use super::*;
fn read_bits(bytes: &[u8], bit_pos: &mut usize, n: u32) -> u32 {
let mut x = 0u32;
for _ in 0..n {
let byte = bytes[*bit_pos >> 3];
let bit = (byte >> (7 - (*bit_pos & 7))) & 1;
x = (x << 1) | u32::from(bit);
*bit_pos += 1;
}
x
}
#[test]
fn put_bits_is_msb_first() {
let mut w = BitWriter::new();
w.put_bits(0b101, 3);
w.put_bits(0b01, 2);
assert_eq!(w.bit_len(), 5);
assert_eq!(w.as_bytes(), &[0xA8]);
}
#[test]
fn byte_align_pads_with_zeros() {
let mut w = BitWriter::new();
w.put_bit(1);
w.byte_align();
assert!(w.is_byte_aligned());
assert_eq!(w.into_bytes(), &[0x80]);
}
#[test]
fn put_bytes_after_alignment() {
let mut w = BitWriter::new();
w.put_bits(0xF, 4);
w.byte_align();
w.put_bytes(&[0x12, 0x34]);
assert_eq!(w.into_bytes(), &[0xF0, 0x12, 0x34]);
}
#[test]
fn roundtrip_various_widths() {
let mut w = BitWriter::new();
let fields = [(0x1, 1u32), (0x2A, 6), (0xFFFF, 16), (0x0, 3), (0x7, 3)];
for &(v, n) in &fields {
w.put_bits(v, n);
}
let bytes = w.into_bytes();
let mut pos = 0usize;
for &(v, n) in &fields {
assert_eq!(read_bits(&bytes, &mut pos, n), v, "field {v:#x}/{n}");
}
}
}