linflate 0.1.3

Fast pure-Rust DEFLATE decompressor — SIMD match-copy, branchless refill, segment-aware
Documentation
//! Pre-built fixed Huffman tables for BTYPE=1 blocks (RFC 1951 §3.2.6).
//!
//! Built once at first use (OnceLock), shared across all threads.

use std::sync::OnceLock;

use super::tables::{
    self, DecompressTables, LITLEN_TABLEBITS, LITLEN_TABLE_SIZE,
    DIST_TABLEBITS, DIST_TABLE_SIZE,
};

/// Fixed litlen code lengths (RFC 1951 §3.2.6):
///   0-143:   8 bits
///   144-255: 9 bits
///   256-279: 7 bits
///   280-287: 8 bits
fn fixed_litlen_lens() -> [u8; 288] {
    let mut lens = [0u8; 288];
    for i in 0..=143   { lens[i] = 8; }
    for i in 144..=255 { lens[i] = 9; }
    for i in 256..=279 { lens[i] = 7; }
    for i in 280..=287 { lens[i] = 8; }
    lens
}

/// Fixed distance code lengths: all 32 symbols have 5 bits.
fn fixed_dist_lens() -> [u8; 32] {
    [5u8; 32]
}

struct FixedTables {
    litlen: [u32; LITLEN_TABLE_SIZE],
    dist: [u32; DIST_TABLE_SIZE],
}

static FIXED: OnceLock<FixedTables> = OnceLock::new();

fn get_fixed() -> &'static FixedTables {
    FIXED.get_or_init(|| {
        let litlen_lens = fixed_litlen_lens();
        let dist_lens = fixed_dist_lens();
        let mut litlen = [0u32; LITLEN_TABLE_SIZE];
        let mut dist = [0u32; DIST_TABLE_SIZE];

        tables::build_decode_table(&litlen_lens, &mut litlen, LITLEN_TABLEBITS, tables::TableKind::Litlen)
            .expect("fixed litlen table build must succeed");
        tables::build_decode_table(&dist_lens, &mut dist, DIST_TABLEBITS, tables::TableKind::Dist)
            .expect("fixed dist table build must succeed");

        FixedTables { litlen, dist }
    })
}

/// Load fixed Huffman tables into the provided DecompressTables.
pub fn load_fixed_tables(tables: &mut DecompressTables) {
    let fixed = get_fixed();
    tables.litlen.copy_from_slice(&fixed.litlen);
    tables.dist[..DIST_TABLE_SIZE].copy_from_slice(&fixed.dist);
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tables::{HUFFDEC_LITERAL, HUFFDEC_END_OF_BLOCK, entry_literal};

    #[test]
    fn fixed_tables_build_successfully() {
        let fixed = get_fixed();
        // Verify a few entries:
        // Symbol 0 (NUL literal, 8-bit code) should be marked as literal
        // Symbol 256 (EOB, 7-bit code) should have EOB flag
        let mut found_eob = false;
        let mut found_lit = false;
        for &entry in fixed.litlen.iter().take(1 << LITLEN_TABLEBITS) {
            if entry & HUFFDEC_END_OF_BLOCK != 0 {
                found_eob = true;
            }
            if entry & HUFFDEC_LITERAL != 0 && entry_literal(entry) == b'A' {
                found_lit = true;
            }
        }
        assert!(found_eob, "should find EOB in fixed litlen table");
        assert!(found_lit, "should find 'A' literal in fixed litlen table");
    }
}