#deflate - DEFLATE Compression/Decompression

    Level: 2 Package: bobzhang/zip/deflate Dependencies: buffer, bitstream, huffman, lz77, crc32, adler32

    #Overview

    The deflate package implements the complete DEFLATE compression algorithm (RFC 1951), combining LZ77 string matching with Huffman coding. This is the core compression format used in ZIP, gzip, and PNG files.

    #Features

    • Full RFC 1951 Compliance: Complete DEFLATE implementation
    • Multiple Compression Modes:
      • Stored (uncompressed) blocks
      • Fixed Huffman coding
      • Dynamic Huffman coding
    • Inflate (Decompression): Fast, streaming decompression
    • Deflate (Compression): Multiple quality levels
    • Checksum Integration: CRC-32 and Adler-32 support
    • Lazy Matching: Optimal LZ77 compression

    #Compression Formats

    #1. Stored Blocks (No Compression)

    deflate_stored(data : BytesView) -> Bytes
    • No compression, just wraps data in DEFLATE format
    • Useful for already-compressed data
    • 5 bytes overhead per 65535-byte block

    #2. Fixed Huffman

    deflate_fixed(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes
    • Uses predefined Huffman trees (RFC 1951)
    • No header overhead for tree description
    • Good for small data or streaming
    • Combines LZ77 + Fixed Huffman

    #3. Dynamic Huffman

    deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes
    • Builds optimal Huffman trees for the data
    • Better compression than fixed Huffman
    • Small header overhead (~50-200 bytes)
    • Best for larger data (>256 bytes)

    #API

    #Inflation (Decompression)

    #inflate(src : BytesView, decompressed_size : Int?) -> Bytes

    Decompress DEFLATE data from a bytes view. Optional expected decompressed size is used purely as a capacity hint; the function still validates actual sizes.

    Helpers inflate_and_crc32 / inflate_and_adler32 were removed to keep the API minimal. Use:
    let decompressed = inflate(compressed[0:compressed.length()], Some(expected_len)) let crc32 = @crc32.bytes_crc32(decompressed[:]) let adler32 = @adler32.bytes_adler32(decompressed[:])

    #Deflation (Compression)

    #deflate_stored(data : BytesView) -> Bytes

    Create uncompressed DEFLATE blocks.

    Use case: When data is incompressible or already compressed (wraps raw slice in a single stored block)

    #deflate_fixed_literals_only(bytes, start, len, is_final) -> Bytes

    Compress using fixed Huffman without LZ77 matching.

    Use case: Testing, education, or when LZ77 provides no benefit

    #deflate_fixed(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes

    Compress using LZ77 + Fixed Huffman.

    Parameters:
    • data - Input slice to compress (use bytes[start:start+len] to select a region)
    • is_final - Whether this is the final block (sets BFINAL)
    • good_match - Early-exit match length threshold
    • max_chain - Maximum LZ77 hash chain traversal depth

    Use case: Fast compression, small data, streaming

    #deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes

    Compress using LZ77 + Dynamic Huffman.

    Parameters: Same fields as deflate_fixed (operates on a BytesView slice)

    Use case: Best compression for larger data (>256 bytes)

    #Helper Functions

    #Symbol Conversion

    pub fn length_to_symbol(length : Int) -> Int pub fn distance_to_symbol(dist : Int) -> Int

    Convert LZ77 match lengths/distances to DEFLATE symbols.

    #Huffman Encoding

    pub fn write_literal_symbol(writer, encoder, symbol) -> Unit pub fn write_length_distance(writer, litlen_encoder, dist_encoder, length, distance) -> Unit

    Low-level functions for writing Huffman-encoded symbols.

    #Usage Examples

    #Basic Compression

    ///|
    test {
    let data = b"Hello, DEFLATE compression!"

    // Compress with default settings
    let compressed = @deflate.deflate_dynamic(
    data[0:data.length()],
    true,
    8,
    1024,
    )

    // Decompress
    let decompressed = @deflate.inflate(compressed[0:compressed.length()])
    @json.inspect(decompressed.length(), content=27)
    }

    #With Checksums

    // Compress and get CRC-32 let compressed = deflate_dynamic(data[0:data.length()], true, 8, 1024) // (Former helper inflate_and_crc32 removed) Explicit pattern: let decompressed = inflate(compressed[0:compressed.length()], decompressed_size=data.length()) let crc = @crc32.bytes_crc32(decompressed[:])

    #Compression Levels

    // Fast compression (Level 1) let fast = deflate_fixed(data, 0, len, true, 4, 128) // Default compression (Level 6) let default = deflate_dynamic(data, 0, len, true, 8, 1024) // Maximum compression (Level 9) let best = deflate_dynamic(data, 0, len, true, 32, 4096)

    #Multi-Block Compression

    let block_size = 32768 let blocks = [] for i = 0; i < data.length(); i += block_size { let len = (data.length() - i).min(block_size) let is_final = (i + len >= data.length()) let block = deflate_dynamic(data, i, len, is_final, 8, 1024) blocks.push(block) } // Concatenate blocks let compressed = concat_bytes(blocks)

    #Compression Quality Parameters

    #good_match - Early Exit Threshold

    ValueBehaviorSpeedCompression
    4Stop at 4-byte matchFastestLower
    8Stop at 8-byte matchBalancedGood
    32Stop at 32-byte matchSlowerBest

    #max_chain - Search Depth

    ValueBehaviorSpeedCompression
    128Check 128 positionsFastestLower
    1024Check 1024 positionsBalancedGood
    4096Check 4096 positionsSlowestBest

    #Algorithm Overview

    #Compression Pipeline

    Input Data ↓ LZ77 String Matching ↓ (literals + length/distance pairs) ↓ Frequency Analysis ↓ Huffman Tree Construction ↓ Huffman Encoding ↓ Bit Packing ↓ Compressed Output

    #Decompression Pipeline

    Compressed Data ↓ Bit Unpacking ↓ Block Header Parsing ↓ Huffman Tree Reconstruction ↓ Symbol Decoding ↓ LZ77 Back-Reference Expansion ↓ Decompressed Output

    #Block Format

    #Block Header (3 bits)

    BFINAL (1 bit): 1 = final block, 0 = more blocks BTYPE (2 bits): 00 = stored, 01 = fixed Huffman, 10 = dynamic Huffman

    #Stored Block

    - Skip to byte boundary - LEN (2 bytes, little-endian) - NLEN (2 bytes, one's complement of LEN) - Raw data (LEN bytes)

    #Fixed Huffman Block

    - Encoded symbols using predefined trees - Symbols: literals (0-255), length (257-285), end-of-block (256) - Distance codes follow length codes

    #Dynamic Huffman Block

    - HLIT (5 bits): # of literal codes - 257 - HDIST (5 bits): # of distance codes - 1
    deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes
    - Literal/length code lengths (encoded) #### `deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes` - Encoded data #### `deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes` let compressed = @deflate.deflate_dynamic(data[0:data.length()], true, 8, 1024) // Compress and get CRC-32 let compressed = deflate_dynamic(data[0:data.length()], true, 8, 1024) let default = deflate_dynamic(data[0:len], true, 8, 1024) let best = deflate_dynamic(data[0:len], true, 32, 4096) let block = deflate_dynamic(data[i:i + len], is_final, 8, 1024) - **Compression**: ~100KB (hash tables + buffers) - **Decompression**: ~300KB (Huffman trees + output buffer) - **No streaming**: Processes entire block in memory ## Testing Run tests with: ```bash moon test deflate

    Tests include:
    • Empty data
    • Single byte
    • Repetitive data (best case for LZ77)
    • Random data (worst case)
    • All compression modes
    • Round-trip compression/decompression
    • Checksum validation
    • RFC 1951 test vectors

    #Dependencies

    • buffer (Level 0) - For byte assembly
    • bitstream (Level 1) - For bit-level I/O
    • huffman (Level 1) - For Huffman coding
    • lz77 (Level 1) - For string matching
    • crc32 (Level 0) - For checksum (optional)
    • adler32 (Level 0) - For checksum (optional)

    #Used By

    • Main zip package - For ZIP file compression
    • Can be used standalone for DEFLATE compression

    #Standards Compliance

    Fully implements:
    • RFC 1951 - DEFLATE Compressed Data Format Specification
      • All three block types (stored, fixed, dynamic)
      • Complete Huffman coding
      • Full LZ77 implementation
      • Proper bit-level encoding

    Compatible with:
    • gzip compressed files
    • PNG image compression
    • ZIP archive format
    • zlib format (with wrapper)

    #Implementation Notes

    #Compression Optimizations

    1. Lazy Matching: Defers encoding to find better matches
    2. Early Exit: Stops search at "good enough" match
    3. Hash Chains: Fast O(1) position lookup
    4. Frequency Counting: Single-pass for dynamic Huffman

    #Decompression Optimizations

    1. Bit Buffering: Reads multiple bytes at once
    2. Tree-Based Lookup: Fast symbol decoding
    3. Output Buffering: Minimizes small writes
    4. In-Place Expansion: LZ77 back-references

    #Known Limitations

    1. Block Size: Processes one block at a time (no streaming)
    2. Memory: Requires full output buffer allocation
    3. No ZIP64: Blocks limited to 4GB (DEFLATE format limit)

    #Future Enhancements

    Potential improvements:
    • Streaming compression/decompression
    • Better hash functions (more sophisticated than 4-byte)
    • Parallel block compression
    • Hardware acceleration (SIMD)
    • Better Huffman tree construction (Package-Merge algorithm)

    #References

    DeflateLevel

    pub(all) enum DeflateLevel {
    None
    Fast
    Default
    Best
    } derive(Eq,
    Debug
    )

    Compression level controlling the speed vs compression ratio tradeoff.

    Each level adjusts multiple parameters that affect both encoding time and output size:
    • LZ77 Parameters: match quality thresholds and search depth
    • Huffman Strategy: fixed vs dynamic trees
    • Block Size Heuristics: when to prefer different compression methods

    Performance Characteristics

    • None: Fastest encoding, largest output (stored blocks only)
    • Fast: Quick encoding, moderate compression (fixed Huffman, shallow search)
    • Default: Balanced speed/size (dynamic Huffman, reasonable search)
    • Best: Slowest encoding, smallest output (dynamic Huffman, deep search)

    Implementation Details

    • None: Uses deflate_stored() exclusively
    • Fast: deflate_fixed() with good_match=4, max_chain=128
    • Default: deflate_dynamic() with good_match=8, max_chain=1024
    • Best: deflate_dynamic() with good_match=32, max_chain=4096

    DeflateLevel::equal

    DeflateLevel::not_equal

    fn DeflateLevel::not_equal(x : DeflateLevel, y : DeflateLevel) -> Bool

    deflate

    fn deflate(data : BytesView, level? : DeflateLevel) -> Bytes raise

    High-level DEFLATE compression with automatic strategy selection.

    This is the main entry point for DEFLATE compression, automatically choosing the optimal compression strategy based on the specified level and input characteristics.

    Strategy Selection Logic

    1. Level-based: DeflateLevel determines algorithm parameters
    2. Size-based: Small inputs (<256 bytes) prefer fixed Huffman to avoid overhead
    3. Final Block: Always produces a single final block (BFINAL=1)

    Algorithm Selection

    • None: Stored blocks (no compression)
    • Fast: Fixed Huffman with minimal LZ77 effort
    • Default/None: Dynamic Huffman for data ≥256 bytes, fixed for smaller
    • Best: Dynamic Huffman with maximum LZ77 search effort

    Parameters

    • data: input slice (BytesView) to compress
    • level: optional compression level (defaults to Default)

    Returns

    Complete DEFLATE stream (RFC 1951) suitable for gzip, zlib, or ZIP usage.

    Limitations

    • Single block output only (no streaming segmentation)
    • Maximum effective input size: ~65KB for stored, unlimited for compressed
    • No preset dictionary support

    deflate_dynamic

    fn deflate_dynamic(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes

    DEFLATE compression using LZ77 + Dynamic Huffman trees per RFC 1951 Section 3.2.7.

    This implements the most sophisticated DEFLATE compression mode (BTYPE=10) which provides optimal compression by building custom Huffman trees tailored to the specific input data characteristics.

    Algorithm Overview

    1. Two-Pass Processing:
      • Pass 1: LZ77 compression + symbol frequency counting
      • Pass 2: Huffman tree construction + final encoding

    2. Dynamic Tree Construction:
      • Build optimal literal/length tree (up to 286 symbols)
      • Build optimal distance tree (up to 30 symbols)
      • Build code length tree to encode the above trees

    3. Header Transmission:
      • HLIT: literal/length tree size
      • HDIST: distance tree size
      • HCLEN: code length tree size
      • Code length tree itself (3-7 bits per symbol)
      • Literal/length tree encoded with code length tree
      • Distance tree encoded with code length tree

    Compression Benefits

    • Adaptive Trees: Huffman codes optimized for actual symbol frequencies
    • Better Compression: Typically 5-15% smaller than fixed Huffman
    • Versatile: Handles both text and binary data effectively

    Overhead Considerations

    • Header Cost: ~100-500 bytes for tree transmission
    • Computation Cost: ~2x encoding time vs fixed Huffman
    • Minimum Size: Most effective for inputs >256 bytes

    Parameters

    • bytes: source data buffer
    • start: starting offset in source
    • len: number of bytes to compress
    • is_final: whether this is the final block in stream
    • good_match: LZ77 "good enough" match length threshold
    • max_chain: LZ77 maximum hash chain search depth

    Returns

    Complete DEFLATE block with dynamic Huffman encoding including tree headers.

    deflate_fixed

    fn deflate_fixed(data : BytesView, is_final : Bool, good_match : Int, max_chain : Int) -> Bytes

    Deflate compression using LZ77 + Fixed Huffman codes per RFC 1951 Section 3.2.6.

    This implements the "fixed Huffman" compression mode (BTYPE=01) which combines:
    1. LZ77 Algorithm: Finds repeated substrings and encodes as length/distance pairs
    2. Fixed Huffman: Uses predefined code tables (no dynamic tree overhead)

    LZ77 Processing

    • Maintains sliding window hash table for fast duplicate detection
    • Configurable match quality vs speed tradeoffs via good_match/max_chain
    • Outputs literal bytes + (length, distance) back-references

    Fixed Huffman Encoding

    • Literal/length symbols: 0-143 (8 bits), 144-255 (9 bits), 256-279 (7 bits), 280-287 (8 bits)
    • Distance symbols: 0-31 (5 bits each)
    • No dynamic tree transmission overhead

    Performance Tuning

    • good_match: stop searching when match >= this length (speed vs compression)
    • max_chain: maximum hash chain traversals (speed vs compression)
    • Higher values = better compression, slower encoding

    Parameters

    • bytes: source data buffer
    • start: starting offset in source
    • len: number of bytes to compress
    • is_final: whether this is the final block in stream
    • good_match: LZ77 "good enough" match length threshold
    • max_chain: LZ77 maximum hash chain search depth

    Returns

    Deflate block with fixed Huffman encoding of LZ77-compressed data.

    deflate_stored

    fn deflate_stored(data : BytesView) -> Bytes raise

    Create stored (uncompressed) deflate blocks per RFC 1951 Section 3.2.4.

    Stored blocks contain raw uncompressed data with a simple header structure:
    • Block header (1 byte): BFINAL bit + BTYPE=00 (stored)
    • LEN (2 bytes, little-endian): actual data length
    • NLEN (2 bytes, little-endian): bitwise complement of LEN for integrity
    • Raw data bytes (LEN bytes)

    The NLEN field provides error detection - decoders verify NLEN = ~LEN. This redundancy is mandated by RFC 1951 for data integrity checking.

    Parameters

    • bytes: source data buffer
    • start: starting offset in source buffer
    • len: number of bytes to compress (max 65535 for single block)

    Limitations

    • Single block only (no chunking for len > 65535)
    • Always marks block as final (BFINAL=1)
    • For larger data, use higher-level deflate() function

    Returns

    Complete deflate stream with stored block containing the specified data slice.

    inflate

    fn inflate(src_view : BytesView, decompressed_size? : Int) -> Bytes raise

    Decompress deflate format data (RFC 1951) Decompress a deflate (RFC 1951) stream segment. Parameters: src_view - BytesView identifying compressed data (no copy performed) decompressed_size - optional expected output size (optimizes allocation / validation) Errors: raises on malformed block headers, invalid Huffman codes, or truncated input.

    zlib_compress

    fn zlib_compress(data : BytesView, level? : DeflateLevel) -> (UInt, Bytes)

    Compress data with zlib wrapper format (RFC 1950) Returns (Adler-32 checksum, compressed bytes)

    zlib format:
    • 2 bytes: CMF + FLG header
    • N bytes: deflate compressed data
    • 4 bytes: Adler-32 checksum (big-endian) Produce a zlib (RFC 1950) wrapped deflate stream. Returns (adler32, bytes) where checksum is of original data.

    zlib_decompress

    fn zlib_decompress(data : BytesView) -> (Bytes, UInt) raise

    Decompress zlib format data (RFC 1950) Returns (decompressed bytes, Adler-32 checksum) Validates header and checksum Parse and decompress a zlib wrapper, validating header & Adler-32. Returns (decompressed bytes, adler32) and raises on header/checksum errors.

    Source Files