archivelib 0.2.0

An implementaton of the Greenleaf ArchiveLib compression/decompression algorithm
Documentation
use std::convert::TryInto;
use std::fmt::Debug;

use crate::support::bit_iter::ToBits;

pub trait BitwiseWrite {
  fn write_bits<B, L>(&mut self, bits: B, bit_count: L) -> std::io::Result<usize>
  where
    B: TryInto<u128> + Debug + Copy,
    L: TryInto<usize> + Debug + Copy;
  fn finalise(&mut self) -> std::io::Result<()>;
}

pub struct BitwiseWriter<W: std::io::Write> {
  inner: W,
  buffer: Vec<bool>,
}

impl<W: std::io::Write> BitwiseWriter<W> {
  pub fn new(w: W) -> Self {
    Self {
      inner: w,
      buffer: Vec::with_capacity(8),
    }
  }
  pub fn commit_buffer(&mut self) -> std::io::Result<usize> {
    if self.buffer.len() >= 8 {
      let mut to_write = Vec::with_capacity(self.buffer.len() / 8);
      while self.buffer.len() >= 8 {
        let this_byte = self.buffer.drain(..8);
        let mut byte = 0;
        for bit in this_byte {
          byte = (byte << 1) | (if bit { 1 } else { 0 })
        }
        to_write.push(byte);
      }
      self.inner.write_all(&to_write)?;
    }
    Ok(self.buffer.len())
  }
}

impl<W: std::io::Write> BitwiseWrite for BitwiseWriter<W> {
  fn write_bits<B, L>(&mut self, bits: B, bit_count: L) -> std::io::Result<usize>
  where
    B: TryInto<u128> + Debug + Copy,
    L: TryInto<usize> + Debug + Copy,
  {
    let bits = bits
      .try_into()
      .map_err(|_| format!("Cannot convert bits({:#X?}) to u128", bits))
      .unwrap();
    let bit_count = bit_count
      .try_into()
      .map_err(|_| format!("Cannot convert bit_count({:#X?}) to usize", bits))
      .unwrap();
    if bit_count > 0 {
      let bit_array = bits.to_bits();
      self
        .buffer
        .extend(bit_array.iter().skip(bit_array.len() - bit_count));
    }
    self.commit_buffer()
  }
  fn finalise(&mut self) -> std::io::Result<()> {
    let unwritten = self.buffer.len() % 8;
    if unwritten > 0 {
      self.write_bits(0, 8 - unwritten)?;
    }
    Ok(())
  }
}