use crate::ErrorKind;
pub(crate) const LIT_ROOT: u32 = 11;
pub(crate) const DIST_ROOT: u32 = 8;
pub(crate) const PRE_ROOT: u32 = 7;
pub(crate) const LIT_ENOUGH: usize = 2342;
pub(crate) const DIST_ENOUGH: usize = 402;
pub(crate) const PRE_ENOUGH: usize = 1 << PRE_ROOT;
pub(crate) const LITERAL: u32 = 1 << 12;
pub(crate) const END_OF_BLOCK: u32 = 1 << 13;
pub(crate) const SUBTABLE: u32 = 1 << 14;
pub(crate) const INVALID: u32 = 1 << 15;
pub(crate) const INVALID_ENTRY: u32 = INVALID;
const LENGTH_BASE: [u16; 29] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131,
163, 195, 227, 258,
];
const LENGTH_EXTRA: [u8; 29] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
];
const DIST_BASE: [u16; 30] = [
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537,
2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
];
const DIST_EXTRA: [u8; 30] = [
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13,
13,
];
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum Alphabet {
LitLen,
Dist,
Precode,
}
const fn entry(alphabet: Alphabet, symbol: usize, len: u32) -> u32 {
match alphabet {
Alphabet::LitLen => {
if symbol < 256 {
((symbol as u32) << 16) | LITERAL | (len << 8) | len
} else if symbol == 256 {
END_OF_BLOCK | (len << 8) | len
} else if symbol < 286 {
let i = symbol - 257;
let extra = LENGTH_EXTRA[i] as u32;
((LENGTH_BASE[i] as u32) << 16) | (len << 8) | (len + extra)
} else {
INVALID | (len << 8) | len
}
}
Alphabet::Dist => {
if symbol < 30 {
let extra = DIST_EXTRA[symbol] as u32;
((DIST_BASE[symbol] as u32) << 16) | (len << 8) | (len + extra)
} else {
INVALID | (len << 8) | len
}
}
Alphabet::Precode => ((symbol as u32) << 16) | (len << 8) | len,
}
}
const fn reverse(code: u32, len: u32) -> u32 {
let mut out = 0;
let mut i = 0;
while i < len {
out |= ((code >> i) & 1) << (len - 1 - i);
i += 1;
}
out
}
pub(crate) const fn build(
lens: &[u8],
table: &mut [u32],
root: u32,
alphabet: Alphabet,
allow_single: bool,
) -> Result<(), ErrorKind> {
let mut count = [0u16; 16];
let mut i = 0;
while i < lens.len() {
count[lens[i] as usize] += 1;
i += 1;
}
count[0] = 0;
let mut left: i32 = 1;
let mut max = 0u32;
let mut len = 1;
while len <= 15 {
left <<= 1;
left -= count[len] as i32;
if left < 0 {
return Err(ErrorKind::InvalidCodeLengths);
}
if count[len] != 0 {
max = len as u32;
}
len += 1;
}
let primary = 1usize << root;
if table.len() < primary {
return Err(ErrorKind::InvalidCodeLengths);
}
let mut j = 0;
while j < primary {
table[j] = INVALID_ENTRY;
j += 1;
}
if max == 0 {
return Ok(());
}
if left > 0 && !(allow_single && max == 1) {
return Err(ErrorKind::InvalidCodeLengths);
}
let mut offsets = [0u16; 16];
let mut len = 1;
while len < 15 {
offsets[len + 1] = offsets[len] + count[len];
len += 1;
}
let mut sorted = [0u16; 288];
let mut used = 0usize;
let mut s = 0;
while s < lens.len() {
let l = lens[s] as usize;
if l != 0 {
sorted[offsets[l] as usize] = s as u16;
offsets[l] += 1;
used += 1;
}
s += 1;
}
let mut remaining = count;
let mut code: u32 = 0;
let mut code_len: u32 = 0;
let mut next_sub = primary;
let mut sub_prefix = u32::MAX;
let mut sub_base = 0usize;
let mut sub_bits = 0u32;
let mut k = 0;
while k < used {
let symbol = sorted[k] as usize;
let len = lens[symbol] as u32;
if code_len != 0 && len > code_len {
code <<= len - code_len;
}
code_len = len;
if len <= root {
let e = entry(alphabet, symbol, len);
let mut j = reverse(code, len) as usize;
while j < primary {
table[j] = e;
j += 1 << len;
}
} else {
let prefix = code >> (len - root);
if prefix != sub_prefix {
let mut bits = len - root;
let mut room: i32 = 1 << bits;
while bits + root < max {
room -= remaining[(bits + root) as usize] as i32;
if room <= 0 {
break;
}
bits += 1;
room <<= 1;
}
let size = 1usize << bits;
if next_sub + size > table.len() || next_sub > 0xFFFF {
return Err(ErrorKind::InvalidCodeLengths);
}
let mut j = 0;
while j < size {
table[next_sub + j] = INVALID_ENTRY;
j += 1;
}
table[reverse(prefix, root) as usize] =
((next_sub as u32) << 16) | SUBTABLE | (bits << 8) | root;
sub_prefix = prefix;
sub_base = next_sub;
sub_bits = bits;
next_sub += size;
}
let sub_len = len - root;
let low = code & ((1 << sub_len) - 1);
let e = entry(alphabet, symbol, sub_len);
let mut j = reverse(low, sub_len) as usize;
while j < (1usize << sub_bits) {
table[sub_base + j] = e;
j += 1 << sub_len;
}
}
remaining[len as usize] -= 1;
code += 1;
k += 1;
}
Ok(())
}
const fn fixed_lit_lens() -> [u8; 288] {
let mut lens = [0u8; 288];
let mut i = 0;
while i < 288 {
lens[i] = if i < 144 {
8
} else if i < 256 {
9
} else if i < 280 {
7
} else {
8
};
i += 1;
}
lens
}
pub(crate) static FIXED_LIT: [u32; 1 << LIT_ROOT] = {
let mut table = [0u32; 1 << LIT_ROOT];
let lens = fixed_lit_lens();
let _ = build(&lens, &mut table, LIT_ROOT, Alphabet::LitLen, true);
table
};
pub(crate) static FIXED_DIST: [u32; 1 << DIST_ROOT] = {
let mut table = [0u32; 1 << DIST_ROOT];
let lens = [5u8; 32];
let _ = build(&lens, &mut table, DIST_ROOT, Alphabet::Dist, true);
table
};
pub(crate) struct Tables {
pub(crate) lit: [u32; LIT_ENOUGH],
pub(crate) dist: [u32; DIST_ENOUGH],
pub(crate) pre: [u32; PRE_ENOUGH],
pub(crate) lens: [u8; 320],
}
impl Tables {
pub(crate) fn new() -> Self {
Self {
lit: [INVALID_ENTRY; LIT_ENOUGH],
dist: [INVALID_ENTRY; DIST_ENOUGH],
pre: [INVALID_ENTRY; PRE_ENOUGH],
lens: [0; 320],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tables_fixed_match_runtime_build() {
let mut lit = [0u32; 1 << LIT_ROOT];
assert_eq!(
build(
&fixed_lit_lens(),
&mut lit,
LIT_ROOT,
Alphabet::LitLen,
true
),
Ok(())
);
assert_eq!(lit, FIXED_LIT);
let mut dist = [0u32; 1 << DIST_ROOT];
assert_eq!(
build(&[5u8; 32], &mut dist, DIST_ROOT, Alphabet::Dist, true),
Ok(())
);
assert_eq!(dist, FIXED_DIST);
}
#[test]
fn test_tables_fixed_lit_entries_decode_known_codes() {
let e = FIXED_LIT[0b0000_1100];
assert_ne!(e & LITERAL, 0);
assert_eq!(e >> 16, 0);
assert_eq!(e & 0xFF, 8);
let e = FIXED_LIT[0];
assert_ne!(e & END_OF_BLOCK, 0);
assert_eq!(e & 0xFF, 7);
}
#[test]
fn test_tables_oversubscribed_rejected() {
let mut t = [0u32; 1 << PRE_ROOT];
assert_eq!(
build(&[1, 1, 1], &mut t, PRE_ROOT, Alphabet::Precode, false),
Err(ErrorKind::InvalidCodeLengths)
);
}
#[test]
fn test_tables_incomplete_rules_follow_zlib() {
let mut t = [0u32; DIST_ENOUGH];
assert_eq!(build(&[1], &mut t, DIST_ROOT, Alphabet::Dist, true), Ok(()));
assert_eq!(t[1], INVALID_ENTRY);
assert_eq!(
build(&[1], &mut t, PRE_ROOT, Alphabet::Precode, false),
Err(ErrorKind::InvalidCodeLengths)
);
assert_eq!(
build(&[2], &mut t, DIST_ROOT, Alphabet::Dist, true),
Err(ErrorKind::InvalidCodeLengths)
);
assert_eq!(
build(&[0, 0], &mut t, DIST_ROOT, Alphabet::Dist, true),
Ok(())
);
assert!(t[..1 << DIST_ROOT].iter().all(|&e| e == INVALID_ENTRY));
}
#[test]
fn test_tables_long_codes_use_subtables_and_fit() {
let mut lens = [0u8; 30];
for (i, l) in lens.iter_mut().enumerate().take(15) {
*l = (i + 1) as u8;
}
lens[15] = 15;
let mut t = [0u32; DIST_ENOUGH];
assert_eq!(
build(&lens, &mut t, DIST_ROOT, Alphabet::Dist, true),
Ok(())
);
assert_ne!(t[0xFF] & SUBTABLE, 0);
}
}