use std::io::{self, BufRead, Write};
#[derive(Debug)]
pub(super) enum PackFault {
Eof,
BitLength,
Few,
Many,
Length,
Read(io::Error),
Write(io::Error),
}
const MAX_LEVELS: usize = 25;
const PEEK: u32 = 12;
const OUT_CHUNK: usize = 64 * 1024;
struct Bits<'r, R: BufRead> {
r: &'r mut R,
acc: u64,
n: u32,
}
impl<R: BufRead> Bits<'_, R> {
fn byte(&mut self) -> Result<Option<u8>, PackFault> {
loop {
match self.r.fill_buf() {
Ok([]) => return Ok(None),
Ok(b) => {
let x = b[0];
self.r.consume(1);
return Ok(Some(x));
}
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(PackFault::Read(e)),
}
}
}
fn need(&mut self, k: u32) -> Result<(), PackFault> {
while self.n < k {
match self.byte()? {
Some(b) => {
self.acc = (self.acc << 8) | u64::from(b);
self.n += 8;
}
None => return Err(PackFault::Eof),
}
}
Ok(())
}
fn bit(&mut self) -> Result<u32, PackFault> {
self.need(1)?;
self.n -= 1;
let b = (self.acc >> self.n) & 1;
self.acc &= (1u64 << self.n) - 1;
Ok(b as u32)
}
}
fn read_exact<R: BufRead>(r: &mut R, buf: &mut [u8]) -> Result<(), PackFault> {
let mut at = 0;
while at < buf.len() {
let avail = match r.fill_buf() {
Ok(b) => b,
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(PackFault::Read(e)),
};
if avail.is_empty() {
return Err(PackFault::Eof);
}
let k = avail.len().min(buf.len() - at);
buf[at..at + k].copy_from_slice(&avail[..k]);
r.consume(k);
at += k;
}
Ok(())
}
pub(super) fn unpack<R: BufRead>(r: &mut R, out: &mut dyn Write) -> Result<u64, PackFault> {
let mut head = [0u8; 7];
read_exact(r, &mut head)?;
let length = u32::from_be_bytes([head[2], head[3], head[4], head[5]]);
let levels = usize::from(head[6]);
if !(1..=MAX_LEVELS).contains(&levels) {
return Err(PackFault::BitLength);
}
let mut counts = [0u8; MAX_LEVELS];
read_exact(r, &mut counts[..levels])?;
let mut leaves: Vec<u64> = counts[..levels].iter().map(|&c| u64::from(c)).collect();
leaves[levels - 1] += 2;
let total: u64 = leaves.iter().sum();
let weight: u64 = leaves
.iter()
.enumerate()
.map(|(l, &c)| c << (levels - 1 - l))
.sum();
let full = 1u64 << levels;
if total > 257 || weight > full {
return Err(PackFault::Many);
}
if weight < full {
return Err(PackFault::Few);
}
let mut symbols = vec![0u8; (total - 1) as usize];
read_exact(r, &mut symbols)?;
let mut inodes = vec![0u64; levels];
for l in (0..levels - 1).rev() {
inodes[l] = (inodes[l + 1] + leaves[l + 1]) / 2;
}
let mut first = Vec::with_capacity(levels);
let mut at = 0u64;
for &c in &leaves {
first.push(at);
at += c;
}
let peek = (levels as u32).min(PEEK);
let mut bits = Bits { r, acc: 0, n: 0 };
let mut buf: Vec<u8> = Vec::with_capacity(OUT_CHUNK);
let mut written = 0u64;
let flush = |buf: &mut Vec<u8>, out: &mut dyn Write| -> Result<(), PackFault> {
out.write_all(buf).map_err(PackFault::Write)?;
buf.clear();
Ok(())
};
loop {
let symbol = (|| -> Result<Option<u8>, PackFault> {
bits.need(peek)?;
let mut code = 0u64;
for l in 0..levels {
code = (code << 1) | u64::from(bits.bit()?);
if code >= inodes[l] {
let idx = first[l] + (code - inodes[l]);
return Ok(symbols.get(idx as usize).copied());
}
}
Err(PackFault::Few)
})();
match symbol {
Ok(Some(b)) => {
buf.push(b);
written += 1;
if buf.len() == OUT_CHUNK {
flush(&mut buf, out)?;
}
}
Ok(None) => break,
Err(e) => {
flush(&mut buf, out)?;
return Err(e);
}
}
}
flush(&mut buf, out)?;
if written as u32 != length {
return Err(PackFault::Length);
}
Ok(written)
}
#[cfg(test)]
mod tests {
use super::*;
fn pk(length: u32, counts: &[u8], syms: &[u8], bits: &[u8]) -> Vec<u8> {
let mut v = vec![0x1f, 0x1e];
v.extend_from_slice(&length.to_be_bytes());
v.push(counts.len() as u8);
v.extend_from_slice(counts);
v.extend_from_slice(syms);
v.extend_from_slice(bits);
v
}
fn run(b: &[u8]) -> (Result<u64, PackFault>, Vec<u8>, usize) {
let mut r = b;
let mut out = Vec::new();
let res = unpack(&mut r, &mut out);
(res, out, r.len())
}
#[test]
fn decodes_to_eob_and_stops_at_its_byte() {
let mut b = pk(8, &[0], b"A", &[0x00, 0x80]);
b.extend_from_slice(b"next");
let (res, out, left) = run(&b);
assert_eq!(res.unwrap(), 8);
assert_eq!(out, b"AAAAAAAA");
assert_eq!(left, 4);
}
#[test]
fn the_length_is_checked_after_eob() {
let (res, out, _) = run(&pk(3, &[0], b"A", &[0x00, 0x80]));
assert!(matches!(res, Err(PackFault::Length)));
assert_eq!(out.len(), 8);
}
#[test]
fn tables_are_judged_as_the_binary_judges_them() {
let cases: &[(&[u8], &str)] = &[
(&[1, 0, 0], "few"), (&[0, 0], "few"),
(&[1, 1], "many"),
(&[2], "many"),
(&[0, 0, 0, 0, 0, 0, 0, 0, 255, 1], "many"), (&[0, 0, 0, 0, 0, 0, 0, 254, 2], "many"), (&[0, 0, 0, 0, 0, 0, 0, 255, 0], "eof"), ];
for (counts, want) in cases {
let n: usize = counts.iter().map(|&c| usize::from(c)).sum::<usize>() + 1;
let syms: Vec<u8> = (0..n).map(|i| i as u8).collect();
let (res, _, _) = run(&pk(1, counts, &syms, &[]));
let got = match res {
Err(PackFault::Few) => "few",
Err(PackFault::Many) => "many",
Err(PackFault::Eof) => "eof",
other => panic!("{counts:?}: {other:?}"),
};
assert_eq!(got, *want, "{counts:?}");
}
for levels in [0usize, 26, 255] {
let (res, _, _) = run(&pk(1, &vec![0; levels], b"A", &[0]));
assert!(matches!(res, Err(PackFault::BitLength)), "{levels}");
}
let (res, _, _) = run(b"\x1f\x1e\0\0\0\x01\0");
assert!(matches!(res, Err(PackFault::BitLength)));
}
#[test]
fn a_symbol_needs_the_peek_width_available() {
for (levels, bytes, want) in [
(25usize, 1usize, 0usize),
(25, 2, 5),
(25, 6, 37),
(8, 1, 1),
(8, 6, 41),
(3, 1, 6),
] {
let mut counts = vec![1u8; levels - 1];
counts.push(0);
let syms: Vec<u8> = (0..levels as u8).map(|i| b'A' + i).collect();
let (res, out, _) = run(&pk(1000, &counts, &syms, &vec![0xff; bytes]));
assert!(matches!(res, Err(PackFault::Eof)), "{levels} {bytes}");
assert_eq!(out.len(), want, "{levels} {bytes}");
}
}
#[test]
fn a_long_code_needs_its_own_length() {
let levels = 25;
let mut counts = vec![1u8; levels - 1];
counts.push(0);
let syms: Vec<u8> = (0..levels as u8).map(|i| b'A' + i).collect();
let mut code = vec![0u8; 12];
code.push(1);
for bytes in 2..14usize {
let bitv: Vec<u8> = code.iter().copied().cycle().take(8 * bytes).collect();
let packed: Vec<u8> = bitv
.chunks(8)
.map(|c| c.iter().fold(0u8, |a, &b| (a << 1) | b))
.collect();
let (res, out, _) = run(&pk(1000, &counts, &syms, &packed));
assert!(matches!(res, Err(PackFault::Eof)));
assert_eq!(out.len(), 8 * bytes / 13, "{bytes}");
assert!(out.iter().all(|&b| b == b'M'));
}
}
}