Compare commits

...

3 commits

Author SHA1 Message Date
6fa7b62d26
add Id compression scheme 2025-10-04 16:00:16 -04:00
bf9212b79a
huffman: fix for inputs with one repeated byte 2025-10-04 15:59:10 -04:00
7f0c1ada52
test: dry clippy allow 2025-10-04 15:33:41 -04:00
5 changed files with 55 additions and 11 deletions

View file

@ -8,4 +8,4 @@ bitvec = "1.0.1"
[dev-dependencies] [dev-dependencies]
quickcheck = "1" quickcheck = "1"
quickcheck_macros = "1" quickcheck_macros = "1"

View file

@ -53,7 +53,12 @@ pub enum Node {
impl Node { impl Node {
fn build(counts: &HashMap<u8, u32>) -> Option<Self> { fn build(counts: &HashMap<u8, u32>) -> Option<Self> {
WeightedNode::build(counts).map(WeightedNode::unburden) let branch = match WeightedNode::build(counts).map(WeightedNode::unburden)? {
leaf @ Node::Leaf { .. } => Self::join(leaf.clone(), leaf),
branch @ Node::Branch { .. } => branch,
};
Some(branch)
} }
/// Write to `buf` the sequence of bits that this tree has assigned `byte`. /// Write to `buf` the sequence of bits that this tree has assigned `byte`.
@ -120,6 +125,13 @@ impl Node {
} }
} }
} }
fn join(path0: Self, path1: Self) -> Self {
Self::Branch {
path0: Box::new(path0),
path1: Box::new(path1),
}
}
} }
impl fmt::Debug for Node { impl fmt::Debug for Node {

28
src/id.rs Normal file
View file

@ -0,0 +1,28 @@
use std::io::Read;
use bitvec::prelude::{BitSlice, BitVec};
use crate::CompressionScheme;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Id;
impl CompressionScheme for Id {
type Header = ();
fn encode(src: &[u8], buf: &mut BitVec) -> Self::Header {
buf.extend_from_bitslice(BitSlice::<u8>::from_slice(src));
}
fn decode(src: &BitSlice, (): &Self::Header, buf: &mut Vec<u8>) {
// TODO: is this actually fine
#[allow(clippy::unbuffered_bytes)]
for byte in src.bytes() {
buf.push(byte.unwrap());
}
}
fn header_size((): &Self::Header) -> usize {
0
}
}

View file

@ -7,6 +7,7 @@ use std::{
mod compression_scheme; mod compression_scheme;
mod freq; mod freq;
mod huffman; mod huffman;
mod id;
mod rle; mod rle;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
@ -15,18 +16,18 @@ use bitvec::vec::BitVec;
pub use compression_scheme::CompressionScheme; pub use compression_scheme::CompressionScheme;
pub use freq::Freq; pub use freq::Freq;
pub use huffman::Huffman; pub use huffman::Huffman;
pub use id::Id;
pub use rle::Rle; pub use rle::Rle;
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
let debug = env::args().any(|arg| arg == "--debug" || arg == "-d"); let debug = env::args().any(|arg| arg == "--debug" || arg == "-d");
let mut buf = Vec::new(); let mut buf = Vec::new();
let len_src = io::stdin().read_to_end(&mut buf)?; io::stdin().read_to_end(&mut buf)?;
println!("Original: {len_src}");
let mut bitbuf = BitVec::new(); let mut bitbuf = BitVec::new();
run::<Id, _>(&buf, &mut bitbuf, "Original", debug);
run::<Rle, _>(&buf, &mut bitbuf, "rle", debug); run::<Rle, _>(&buf, &mut bitbuf, "rle", debug);
run::<Freq, _>(&buf, &mut bitbuf, "freq", debug); run::<Freq, _>(&buf, &mut bitbuf, "freq", debug);
run::<Huffman, _>(&buf, &mut bitbuf, "Huffman", debug); run::<Huffman, _>(&buf, &mut bitbuf, "Huffman", debug);
@ -42,7 +43,7 @@ where
bitbuf.clear(); bitbuf.clear();
let header = Scheme::encode(buf, bitbuf); let header = Scheme::encode(buf, bitbuf);
let len_freq = Scheme::header_size(&header) + bitbuf.len().div_ceil(8); let len_freq = Scheme::header_size(&header) + bitbuf.len().div_ceil(8);
println!("{name}'d: {len_freq}"); println!("{name:>10}: {len_freq:>10}");
if debug { if debug {
eprintln!("{name} header: {header:#?}\n"); eprintln!("{name} header: {header:#?}\n");
} }

View file

@ -1,21 +1,24 @@
#![allow(clippy::needless_pass_by_value)]
use crate::{CompressionScheme, Freq, Huffman, Id, Rle};
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
use crate::{CompressionScheme, Freq, Huffman, Rle};
#[allow(clippy::needless_pass_by_value)]
#[quickcheck] #[quickcheck]
fn roundtrip_freq(src: Vec<u8>) -> bool { fn roundtrip_freq(src: Vec<u8>) -> bool {
Freq::idempotent_on(&src) Freq::idempotent_on(&src)
} }
#[allow(clippy::needless_pass_by_value)]
#[quickcheck] #[quickcheck]
fn roundtrip_rle(src: Vec<u8>) -> bool { fn roundtrip_rle(src: Vec<u8>) -> bool {
Rle::idempotent_on(&src) Rle::idempotent_on(&src)
} }
#[allow(clippy::needless_pass_by_value)]
#[quickcheck] #[quickcheck]
fn roundtrip_huffman(src: Vec<u8>) -> bool { fn roundtrip_huffman(src: Vec<u8>) -> bool {
Huffman::idempotent_on(&src) Huffman::idempotent_on(&src)
} }
#[quickcheck]
fn roundtrip_id(src: Vec<u8>) -> bool {
Id::idempotent_on(&src)
}