use crate::bitstream::BitWriter;
use crate::huffman;
pub fn encode(out: &mut BitWriter, prev_lengths: &[u8], new_lengths: &[u8]) {
assert_eq!(prev_lengths.len(), new_lengths.len(), "prev/new must be equal-length slices");
let mut symbols = plan_symbols(prev_lengths, new_lengths);
ensure_multi_symbol(&mut symbols, prev_lengths);
let mut freqs = [0u32; 20];
for op in &symbols {
freqs[op.symbol as usize] += 1;
if op.symbol == 19 {
freqs[op.extra2 as usize] += 1;
}
}
let pretree_lengths = huffman::build_path_lengths(&freqs);
let pretree_codes = huffman::build_codes(&pretree_lengths);
for &l in pretree_lengths.iter().take(20) {
out.write_bits(l as u32, 4);
}
for _ in pretree_lengths.len()..20 {
out.write_bits(0, 4);
}
for op in &symbols {
let c = pretree_codes[op.symbol as usize];
out.write_bits(c.value, c.len);
match op.symbol {
17 => out.write_bits(op.extra, 4),
18 => out.write_bits(op.extra, 5),
19 => {
out.write_bits(op.extra, 1);
let follow = pretree_codes[op.extra2 as usize];
out.write_bits(follow.value, follow.len);
}
_ => {}
}
}
}
#[derive(Debug, Clone, Copy)]
struct Op {
symbol: u8,
extra: u32,
extra2: u8,
}
fn plan_symbols(prev: &[u8], new: &[u8]) -> Vec<Op> {
let mut ops = Vec::with_capacity(new.len());
let mut i = 0;
while i < new.len() {
if new[i] == 0 {
let mut run = 1;
while i + run < new.len() && new[i + run] == 0 {
run += 1;
}
let mut remaining = run;
while remaining >= 20 {
let count = remaining.min(20 + 31);
let extra = (count - 20) as u32;
ops.push(Op { symbol: 18, extra, extra2: 0 });
remaining -= count;
i += count;
}
while remaining >= 4 {
let count = remaining.min(4 + 15);
let extra = (count - 4) as u32;
ops.push(Op { symbol: 17, extra, extra2: 0 });
remaining -= count;
i += count;
}
for _ in 0..remaining {
let delta = modsub(prev[i], 0);
ops.push(Op { symbol: delta, extra: 0, extra2: 0 });
i += 1;
}
} else {
let value = new[i];
let mut run = 1;
while i + run < new.len() && new[i + run] == value {
run += 1;
}
let cover = cover_same_run(run);
for _ in 0..cover.fives {
ops.push(Op { symbol: 19, extra: 1, extra2: modsub(prev[i], value) });
i += 5;
}
for _ in 0..cover.fours {
ops.push(Op { symbol: 19, extra: 0, extra2: modsub(prev[i], value) });
i += 4;
}
for _ in 0..cover.literals {
ops.push(Op { symbol: modsub(prev[i], value), extra: 0, extra2: 0 });
i += 1;
}
}
}
ops
}
struct CoverPlan {
fours: usize,
fives: usize,
literals: usize,
}
fn cover_same_run(run: usize) -> CoverPlan {
let (fours, fives, literals) = match run {
0 => (0, 0, 0),
1..=3 => (0, 0, run),
4 => (1, 0, 0),
5 => (0, 1, 0),
6 => (0, 1, 1),
7 => (0, 1, 2),
8 => (2, 0, 0),
9 => (1, 1, 0),
10 => (0, 2, 0),
11 => (0, 2, 1),
_ => match run % 4 {
0 => (run / 4, 0, 0),
1 => ((run - 5) / 4, 1, 0),
2 => ((run - 10) / 4, 2, 0),
3 => ((run - 15) / 4, 3, 0),
_ => unreachable!(),
},
};
CoverPlan { fours, fives, literals }
}
fn modsub(prev: u8, new: u8) -> u8 {
((17 + prev as i16 - new as i16).rem_euclid(17)) as u8
}
fn ensure_multi_symbol(ops: &mut Vec<Op>, prev_lengths: &[u8]) {
let first = match ops.first() {
Some(op) => op.symbol,
None => return,
};
if ops.iter().any(|o| o.symbol != first) {
return;
}
for idx in (0..ops.len()).rev() {
let op = ops[idx];
if (op.symbol == 17 || op.symbol == 18) && op.extra > 0 {
ops[idx].extra -= 1;
let stolen = end_position_after_ops(ops, idx);
let literal_sym = prev_lengths.get(stolen).map_or(0, |&p| p % 17);
ops.insert(idx + 1, Op { symbol: literal_sym, extra: 0, extra2: 0 });
return;
}
}
if first == 17 || first == 18 {
let last_op = *ops.last().unwrap();
let cover = op_cover(&last_op);
let end_pos = end_position_after_ops(ops, ops.len() - 1);
let start_pos = end_pos.saturating_sub(cover);
ops.pop();
for p in start_pos..end_pos {
let literal_sym = prev_lengths.get(p).map_or(0, |&v| v % 17);
ops.push(Op { symbol: literal_sym, extra: 0, extra2: 0 });
}
if ops.iter().any(|o| o.symbol != first) {
return;
}
}
if first <= 16 && ops.len() >= 4 {
let new_op = Op { symbol: 19, extra: 0, extra2: first };
ops.drain(0..4);
ops.insert(0, new_op);
}
}
fn end_position_after_ops(ops: &[Op], idx: usize) -> usize {
let mut pos = 0usize;
for (i, op) in ops.iter().enumerate() {
pos += op_cover(op);
if i == idx {
break;
}
}
pos
}
fn op_cover(op: &Op) -> usize {
match op.symbol {
17 => 4 + op.extra as usize,
18 => 20 + op.extra as usize,
19 => 4 + op.extra as usize,
_ => 1,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn modsub_basic() {
assert_eq!(modsub(0, 0), 0);
assert_eq!(modsub(5, 3), 2);
assert_eq!(modsub(3, 5), (17 + 3 - 5) as u8 % 17); assert_eq!(modsub(0, 8), 9);
}
#[test]
fn encodes_all_zeros_without_panic() {
let prev = vec![0u8; 256];
let new = vec![0u8; 256];
let mut w = BitWriter::new();
encode(&mut w, &prev, &new);
let _ = w.finish();
}
}