use crate::compliance::iab_tcf::{BitField, Error, Result};
pub struct BitWriter {
data: Vec<u8>,
current_byte: u8,
bit_pos: u8,
}
impl BitWriter {
#[must_use]
pub fn new() -> Self {
Self {
data: Vec::new(),
current_byte: 0,
bit_pos: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
current_byte: 0,
bit_pos: 0,
}
}
pub fn write_bit(&mut self, bit: bool) -> Result<()> {
if bit {
self.current_byte |= 1 << (7 - self.bit_pos);
}
self.bit_pos += 1;
if self.bit_pos == 8 {
self.data.push(self.current_byte);
self.current_byte = 0;
self.bit_pos = 0;
}
Ok(())
}
pub fn write_bits(&mut self, value: u64, num_bits: u8) -> Result<()> {
if num_bits > 64 {
return Err(Error::TooManyBits(num_bits));
}
if num_bits == 0 {
return Ok(());
}
for i in (0..num_bits).rev() {
let bit = ((value >> i) & 1) == 1;
self.write_bit(bit)?;
}
Ok(())
}
pub fn write_bitfield(&mut self, field: &BitField) -> Result<()> {
for i in 0..field.len() {
self.write_bit(field.get(i))?;
}
Ok(())
}
#[must_use]
pub fn to_bytes(mut self) -> Vec<u8> {
if self.bit_pos > 0 {
self.data.push(self.current_byte);
}
self.data
}
#[must_use]
pub fn byte_len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn bit_len(&self) -> usize {
self.data.len() * 8 + self.bit_pos as usize
}
#[must_use]
pub fn position(&self) -> (usize, u8) {
(self.data.len(), self.bit_pos)
}
}
impl Default for BitWriter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_write_single_bits() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bit(true)?; writer.write_bit(false)?; writer.write_bit(true)?; writer.write_bit(true)?; writer.write_bit(false)?; writer.write_bit(true)?; writer.write_bit(false)?; writer.write_bit(false)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 1);
assert_eq!(bytes[0], 0b1011_0100);
Ok(())
}
#[test]
fn test_write_multiple_bits() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bits(0b1011, 4)?;
writer.write_bits(0b0100_1100, 8)?;
writer.write_bits(0b1010, 4)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 2);
assert_eq!(bytes[0], 0b1011_0100);
assert_eq!(bytes[1], 0b1100_1010);
Ok(())
}
#[test]
fn test_write_across_byte_boundary() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bits(0b11_1111, 6)?; writer.write_bits(0b1100, 4)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 2);
assert_eq!(bytes[0], 0b1111_1111);
assert_eq!(bytes[1], 0b0000_0000);
Ok(())
}
#[test]
fn test_write_bitfield() -> Result<()> {
let mut field = BitField::new(8);
field.set(0, true);
field.set(1, true);
field.set(2, true);
field.set(3, true);
let mut writer = BitWriter::new();
writer.write_bitfield(&field)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 1);
assert_eq!(bytes[0], 0b1111_0000);
Ok(())
}
#[test]
fn test_partial_byte_padding() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bits(0b1010, 4)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 1);
assert_eq!(bytes[0], 0b1010_0000);
Ok(())
}
#[test]
fn test_too_many_bits_error() {
let mut writer = BitWriter::new();
assert!(matches!(
writer.write_bits(0, 65),
Err(Error::TooManyBits(65))
));
}
#[test]
fn test_write_zero_bits() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bits(0xFF, 0)?;
let bytes = writer.to_bytes();
assert_eq!(bytes.len(), 0);
Ok(())
}
#[test]
fn test_bit_len() -> Result<()> {
let mut writer = BitWriter::new();
assert_eq!(writer.bit_len(), 0);
writer.write_bits(0b1111, 4)?;
assert_eq!(writer.bit_len(), 4);
writer.write_bits(0b1010, 4)?;
assert_eq!(writer.bit_len(), 8);
writer.write_bits(0b11, 2)?;
assert_eq!(writer.bit_len(), 10);
Ok(())
}
#[test]
fn test_position() -> Result<()> {
let mut writer = BitWriter::new();
writer.write_bits(0b11111, 5)?;
let (byte_count, bit_pos) = writer.position();
assert_eq!(byte_count, 0);
assert_eq!(bit_pos, 5);
writer.write_bits(0b1111, 4)?;
let (byte_count, bit_pos) = writer.position();
assert_eq!(byte_count, 1);
assert_eq!(bit_pos, 1);
Ok(())
}
#[test]
fn test_roundtrip_with_bitreader() -> Result<()> {
use crate::compliance::iab_tcf::BitReader;
let mut writer = BitWriter::new();
writer.write_bit(true)?;
writer.write_bits(0b1010, 4)?;
writer.write_bit(false)?;
writer.write_bits(0xFF, 8)?;
let bytes = writer.to_bytes();
let mut reader = BitReader::new(&bytes);
assert!(reader.read_bit()?);
assert_eq!(reader.read_bits(4)?, 0b1010);
assert!(!(reader.read_bit()?));
assert_eq!(reader.read_bits(8)?, 0xFF);
Ok(())
}
}