use crate::error::{BitReaderError, BitWriterError};
pub struct BitReader<'a> {
buffer: &'a [u8],
bit_cursor: usize,
}
impl<'a> BitReader<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
Self {
buffer,
bit_cursor: 0,
}
}
pub fn seek(&mut self, bit_offset: usize) {
self.bit_cursor = bit_offset;
}
pub fn bit_cursor(&self) -> usize {
self.bit_cursor
}
pub fn bits_remaining(&self) -> usize {
(self.buffer.len() * 8).saturating_sub(self.bit_cursor)
}
pub fn read_u64(&mut self, num_bits: u8) -> Result<u64, BitReaderError> {
if !(1..=64).contains(&num_bits) {
return Err(BitReaderError::TooLongForType {
max: 64,
asked: num_bits,
});
}
let buffer_len_bits = self.buffer.len() * 8;
let read_end_bit = self.bit_cursor.saturating_add(num_bits as usize);
if read_end_bit > buffer_len_bits {
return Err(BitReaderError::OutOfBounds {
asked: num_bits as usize,
available: buffer_len_bits.saturating_sub(self.bit_cursor),
});
}
let mut result: u64 = 0;
let mut bits_read = 0;
while bits_read < num_bits {
let current_byte_index = (self.bit_cursor + bits_read as usize) / 8;
let current_bit_offset = (self.bit_cursor + bits_read as usize) % 8;
let byte = self.buffer[current_byte_index];
let bits_to_read_this_iteration =
(8 - current_bit_offset).min(num_bits as usize - bits_read as usize);
let mask = ((1u16 << bits_to_read_this_iteration) - 1) as u8;
let masked_value = (byte >> current_bit_offset) & mask;
result |= (masked_value as u64) << bits_read;
bits_read += bits_to_read_this_iteration as u8;
}
self.bit_cursor += num_bits as usize;
Ok(result)
}
pub fn read_u8(&mut self, num_bits: u8) -> Result<u8, BitReaderError> {
if num_bits > 8 {
return Err(BitReaderError::TooLongForType {
max: 8,
asked: num_bits,
});
}
self.read_u64(num_bits).map(|val| val as u8)
}
pub fn read_u16(&mut self, num_bits: u8) -> Result<u16, BitReaderError> {
if num_bits > 16 {
return Err(BitReaderError::TooLongForType {
max: 16,
asked: num_bits,
});
}
self.read_u64(num_bits).map(|val| val as u16)
}
pub fn read_u32(&mut self, num_bits: u8) -> Result<u32, BitReaderError> {
if num_bits > 32 {
return Err(BitReaderError::TooLongForType {
max: 32,
asked: num_bits,
});
}
self.read_u64(num_bits).map(|val| val as u32)
}
pub fn advance(&mut self, length: u8) -> Result<(), BitReaderError> {
if !(1..=64).contains(&length) {
return Err(BitReaderError::TooLongForType {
max: 64,
asked: length,
});
}
let buffer_len_bits = self.buffer.len() * 8;
let new_cursor_pos = self.bit_cursor.saturating_add(length as usize);
if new_cursor_pos > buffer_len_bits {
return Err(BitReaderError::OutOfBounds {
asked: length as usize,
available: buffer_len_bits.saturating_sub(self.bit_cursor),
});
}
self.bit_cursor = new_cursor_pos;
Ok(())
}
pub fn read_slice(&mut self, len: usize) -> Result<&'a [u8], BitReaderError> {
if !self.bit_cursor.is_multiple_of(8) {
return Err(BitReaderError::NonAlignedBit {
cursor: self.bit_cursor,
});
}
let byte_start = self.bit_cursor / 8;
let byte_end = byte_start.saturating_add(len);
if byte_end > self.buffer.len() {
return Err(BitReaderError::OutOfBounds {
asked: byte_end,
available: self.buffer.len(),
});
}
let slice = &self.buffer[byte_start..byte_end];
self.bit_cursor += len * 8;
Ok(slice)
}
}
pub struct BitWriter<'a> {
buffer: &'a mut [u8],
bit_cursor: usize,
}
impl<'a> BitWriter<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
Self {
buffer,
bit_cursor: 0,
}
}
pub fn bit_cursor(&self) -> usize {
self.bit_cursor
}
pub fn write_u64(&mut self, value: u64, num_bits: u8) -> Result<(), BitWriterError> {
if !(1..=64).contains(&num_bits) {
return Err(BitWriterError::TooLongForType {
max: 64,
asked: num_bits,
});
}
let buffer_len_bits = self.buffer.len() * 8;
let write_end_bit = self.bit_cursor + num_bits as usize;
if write_end_bit > buffer_len_bits {
return Err(BitWriterError::OutOfBounds {
asked: num_bits as usize,
available: buffer_len_bits - self.bit_cursor,
});
}
let mut val_to_write = value;
let mut bits_write = 0;
while bits_write < num_bits {
let current_byte_index = (self.bit_cursor + bits_write as usize) / 8;
let current_bit_offset = (self.bit_cursor + bits_write as usize) % 8;
let bits_to_write_this_iteration =
(8 - current_bit_offset).min(num_bits as usize - bits_write as usize);
let mask = ((1u16 << bits_to_write_this_iteration) - 1) as u8;
self.buffer[current_byte_index] &= !(mask << current_bit_offset);
self.buffer[current_byte_index] |= (val_to_write as u8 & mask) << current_bit_offset;
val_to_write >>= bits_to_write_this_iteration;
bits_write += bits_to_write_this_iteration as u8;
}
self.bit_cursor += num_bits as usize;
Ok(())
}
pub fn write_u8(&mut self, value: u8, num_bits: u8) -> Result<(), BitWriterError> {
if num_bits > 8 {
return Err(BitWriterError::TooLongForType {
max: 8,
asked: num_bits,
});
}
self.write_u64(value as u64, num_bits)
}
pub fn write_u16(&mut self, value: u16, num_bits: u8) -> Result<(), BitWriterError> {
if num_bits > 16 {
return Err(BitWriterError::TooLongForType {
max: 16,
asked: num_bits,
});
}
self.write_u64(value as u64, num_bits)
}
pub fn write_u32(&mut self, value: u32, num_bits: u8) -> Result<(), BitWriterError> {
if num_bits > 32 {
return Err(BitWriterError::TooLongForType {
max: 32,
asked: num_bits,
});
}
self.write_u64(value as u64, num_bits)
}
pub fn advance(&mut self, length: u8) -> Result<(), BitWriterError> {
if !(1..=64).contains(&length) {
return Err(BitWriterError::TooLongForType {
max: 64,
asked: length,
});
}
let buffer_len_bits = self.buffer.len() * 8;
let new_cursor_pos = self.bit_cursor + length as usize;
if new_cursor_pos > buffer_len_bits {
return Err(BitWriterError::OutOfBounds {
asked: length as usize,
available: buffer_len_bits - self.bit_cursor,
});
}
self.bit_cursor = new_cursor_pos;
Ok(())
}
pub fn write_slice(&mut self, slice: &[u8]) -> Result<(), BitWriterError> {
if !self.bit_cursor.is_multiple_of(8) {
return Err(BitWriterError::NonAlignedBit {
cursor: self.bit_cursor,
});
}
let byte_start = self.bit_cursor / 8;
let byte_end = byte_start + slice.len();
if byte_end > self.buffer.len() {
return Err(BitWriterError::OutOfBounds {
asked: byte_end,
available: self.buffer.len(),
});
}
self.buffer[byte_start..byte_end].copy_from_slice(slice);
self.bit_cursor += slice.len() * 8;
Ok(())
}
}
#[cfg(test)]
#[path = "tests.rs"]
mod tests;