Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy

znippy-zip-fixtures/src/lib.rs

//! Synthetic ZIP / JAR / wheel fixtures — the ONE writer.
//!
//! # Why this crate exists
//!
//! Four files in this workspace carried their own hand-rolled `crc32`, and three
//! of them carried a byte-for-byte identical ~55-line `build_zip_stored`:
//!
//! | file | crc32 | stored | deflated |
//! |---|---|---|---|
//! | `tests/tests/maven_bench.rs`          | ✔ | ✔ | — |
//! | `tests/tests/plugin_read_api_test.rs` | ✔ | ✔ | — |
//! | `tests/tests/plugin_deflate_entry.rs` | ✔ | — | ✔ |
//! | `xtask/examples/nornir-bench.rs`      | ✔ | ✔ | — |
//!
//! LAW 5 (reuse, do not twin) says share it. The split above is also *how the
//! bug happened*: `plugin_deflate_entry.rs` grew the only method-8 builder in
//! the workspace and nobody carried it to the bench, so `MavenExtractBench`
//! timed a **stored** JAR — i.e. it timed ZIP central-directory parsing and
//! never once ran an inflate, while reporting a `speedup_x` for the house
//! DEFLATE core. A number that is real, precise, and about nothing.
//!
//! # Fixed by construction
//!
//! There is exactly one ZIP writer here — [`build_zip_entry`] — and the stored
//! and deflated constructors are thin calls into it that differ only in the
//! method field and the payload. A future "stored where deflate was meant"
//! cannot come back as a second, drifting copy of the container code.
//!
//! Decompression is never done here: this crate only ever *produces* input for
//! the house engine (`linflate` / `ljar` / `lzip-parallel`). `miniz_oxide` is
//! the encoder only.

/// ZIP compression method 0 — stored, no compression.
pub const METHOD_STORED: u16 = 0;
/// ZIP compression method 8 — raw DEFLATE (no zlib wrapper), per APPNOTE.
pub const METHOD_DEFLATE: u16 = 8;

/// The ZIP/PKZIP CRC-32 (reflected, polynomial 0xEDB8_8320).
pub fn crc32(data: &[u8]) -> u32 {
    let mut crc = 0xFFFF_FFFFu32;
    for &byte in data {
        crc ^= byte as u32;
        for _ in 0..8 {
            crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB8_8320 } else { crc >> 1 };
        }
    }
    !crc
}

/// Build a minimal, valid single-entry ZIP: local file header, payload, central
/// directory header, end-of-central-directory.
///
/// `uncompressed` supplies the CRC and the uncompressed size; `payload` is the
/// bytes actually stored in the entry, and `method` says how a reader should
/// interpret them. Every fixture in the workspace goes through here.
pub fn build_zip_entry(
    entry_name: &str,
    uncompressed: &[u8],
    method: u16,
    payload: &[u8],
) -> Vec<u8> {
    let name = entry_name.as_bytes();
    let crc = crc32(uncompressed);
    let usize_ = uncompressed.len() as u32;
    let csize = payload.len() as u32;

    let mut out = Vec::new();

    // ── Local file header ──
    let local_offset = out.len() as u32;
    out.extend_from_slice(b"PK\x03\x04");
    out.extend_from_slice(&20u16.to_le_bytes()); // version needed
    out.extend_from_slice(&0u16.to_le_bytes()); // flags
    out.extend_from_slice(&method.to_le_bytes()); // compression method
    out.extend_from_slice(&0u16.to_le_bytes()); // mod time
    out.extend_from_slice(&0u16.to_le_bytes()); // mod date
    out.extend_from_slice(&crc.to_le_bytes());
    out.extend_from_slice(&csize.to_le_bytes());
    out.extend_from_slice(&usize_.to_le_bytes());
    out.extend_from_slice(&(name.len() as u16).to_le_bytes());
    out.extend_from_slice(&0u16.to_le_bytes()); // extra field length
    out.extend_from_slice(name);
    out.extend_from_slice(payload);

    // ── Central directory header ──
    let cd_offset = out.len() as u32;
    out.extend_from_slice(b"PK\x01\x02");
    out.extend_from_slice(&20u16.to_le_bytes()); // version made by
    out.extend_from_slice(&20u16.to_le_bytes()); // version needed
    out.extend_from_slice(&0u16.to_le_bytes()); // flags
    out.extend_from_slice(&method.to_le_bytes()); // compression method
    out.extend_from_slice(&0u16.to_le_bytes()); // mod time
    out.extend_from_slice(&0u16.to_le_bytes()); // mod date
    out.extend_from_slice(&crc.to_le_bytes());
    out.extend_from_slice(&csize.to_le_bytes());
    out.extend_from_slice(&usize_.to_le_bytes());
    out.extend_from_slice(&(name.len() as u16).to_le_bytes());
    out.extend_from_slice(&0u16.to_le_bytes()); // extra field length
    out.extend_from_slice(&0u16.to_le_bytes()); // comment length
    out.extend_from_slice(&0u16.to_le_bytes()); // disk start
    out.extend_from_slice(&0u16.to_le_bytes()); // internal attr
    out.extend_from_slice(&0u32.to_le_bytes()); // external attr
    out.extend_from_slice(&local_offset.to_le_bytes());
    out.extend_from_slice(name);

    // ── End of central directory ──
    let cd_size = out.len() as u32 - cd_offset;
    out.extend_from_slice(b"PK\x05\x06");
    out.extend_from_slice(&0u16.to_le_bytes()); // disk number
    out.extend_from_slice(&0u16.to_le_bytes()); // disk with cd
    out.extend_from_slice(&1u16.to_le_bytes()); // entries on disk
    out.extend_from_slice(&1u16.to_le_bytes()); // total entries
    out.extend_from_slice(&cd_size.to_le_bytes());
    out.extend_from_slice(&cd_offset.to_le_bytes());
    out.extend_from_slice(&0u16.to_le_bytes()); // comment length

    out
}

/// A single-entry ZIP whose entry is **stored** (method 0).
pub fn build_zip_stored(entry_name: &str, entry_data: &[u8]) -> Vec<u8> {
    build_zip_entry(entry_name, entry_data, METHOD_STORED, entry_data)
}

/// A single-entry ZIP whose entry is really **DEFLATE-compressed** (method 8,
/// raw deflate stream, no zlib wrapper).
pub fn build_zip_deflated(entry_name: &str, entry_data: &[u8]) -> Vec<u8> {
    let deflated = miniz_oxide::deflate::compress_to_vec(entry_data, 6);
    build_zip_entry(entry_name, entry_data, METHOD_DEFLATE, &deflated)
}

/// Same as [`build_zip_deflated`], but the compressed payload is supplied
/// verbatim — lets a test feed a CORRUPTED deflate stream through an otherwise
/// perfectly valid ZIP container.
pub fn build_zip_method8_with_payload(
    entry_name: &str,
    uncompressed: &[u8],
    payload: &[u8],
) -> Vec<u8> {
    build_zip_entry(entry_name, uncompressed, METHOD_DEFLATE, payload)
}

/// Read the compression method / compressed size / uncompressed size straight
/// back out of a fixture's local file header.
///
/// Exists so a caller can ASSERT what it built rather than trust the function
/// name — the whole maven-bench defect was a fixture that said "jar" and meant
/// "stored". Returns `(method, compressed_size, uncompressed_size)`.
pub fn read_local_header(zip: &[u8]) -> (u16, u32, u32) {
    assert!(zip.len() >= 30 && &zip[0..4] == b"PK\x03\x04", "not a local file header");
    let m = u16::from_le_bytes([zip[8], zip[9]]);
    let c = u32::from_le_bytes([zip[18], zip[19], zip[20], zip[21]]);
    let u = u32::from_le_bytes([zip[22], zip[23], zip[24], zip[25]]);
    (m, c, u)
}

/// The small canonical `pom.xml` every maven fixture used to inline by hand.
pub fn pom_xml(group: &str, artifact: &str, version: &str) -> String {
    format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project>\n  \
         <groupId>{group}</groupId>\n  <artifactId>{artifact}</artifactId>\n  \
         <version>{version}</version>\n  <packaging>jar</packaging>\n</project>"
    )
}

/// A pom big and repetitive enough that the encoder emits a real dynamic-Huffman
/// DEFLATE block with long back-references — i.e. it exercises `linflate`'s
/// fastloop and SIMD match-copy, not just a stored block. ~200 dependencies,
/// ~20 kB uncompressed.
pub fn big_pom(group: &str, artifact: &str, version: &str) -> String {
    let mut deps = String::new();
    for i in 0..200 {
        deps.push_str(&format!(
            "    <dependency>\n      <groupId>org.example.dep{i}</groupId>\n      \
             <artifactId>artifact-{i}</artifactId>\n      <version>1.0.{i}</version>\n    \
             </dependency>\n"
        ));
    }
    format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project>\n  \
         <groupId>{group}</groupId>\n  <artifactId>{artifact}</artifactId>\n  \
         <version>{version}</version>\n  <packaging>jar</packaging>\n  \
         <dependencies>\n{deps}  </dependencies>\n</project>"
    )
}

/// The canonical maven layout path for a pom inside a JAR.
pub fn pom_path(group: &str, artifact: &str) -> String {
    format!("META-INF/maven/{group}/{artifact}/pom.xml")
}

#[cfg(test)]
mod tests {
    use super::*;

    /// The standard CRC-32 check vector. A silently wrong CRC would make every
    /// fixture in the workspace decode-reject in a way that looks like a codec bug.
    #[test]
    fn crc32_matches_the_standard_check_vector() {
        assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
    }

    /// A "stored" fixture must really be method 0 with csize == usize.
    #[test]
    fn stored_fixture_is_really_stored() {
        let data = b"hello world";
        let zip = build_zip_stored("a.txt", data);
        let (method, csize, usize_) = read_local_header(&zip);
        assert_eq!(method, METHOD_STORED);
        assert_eq!(csize, data.len() as u32);
        assert_eq!(usize_, data.len() as u32);
        // The payload sits verbatim right after the name.
        assert!(zip.windows(data.len()).any(|w| w == data));
    }

    /// THE regression this crate was extracted for: a "deflated" fixture must
    /// really be method 8 AND really be smaller than the input. A builder that
    /// quietly emitted a stored entry would still parse, still return the right
    /// metadata, and still produce a plausible benchmark number — while never
    /// running an inflate. `csize < usize` is the applied-output check that
    /// cannot be satisfied by a stored entry.
    #[test]
    fn deflated_fixture_really_deflates() {
        let pom = big_pom("com.example", "mylib", "1.2.3");
        let zip = build_zip_deflated(&pom_path("com.example", "mylib"), pom.as_bytes());
        let (method, csize, usize_) = read_local_header(&zip);
        assert_eq!(method, METHOD_DEFLATE, "fixture must be method 8");
        assert_eq!(usize_, pom.len() as u32);
        assert!(
            csize < usize_ / 2,
            "a repetitive 200-dependency pom must compress to well under half \
             ({csize} vs {usize_}) — if it did not, the 'deflated' fixture is \
             stored in disguise and no inflate is being exercised"
        );
    }

    /// The corrupted-payload builder keeps a valid container but a broken stream.
    #[test]
    fn method8_with_payload_keeps_the_declared_uncompressed_size() {
        let data = b"the real bytes";
        let zip = build_zip_method8_with_payload("a.txt", data, b"\xff\xff\xff\xff");
        let (method, csize, usize_) = read_local_header(&zip);
        assert_eq!(method, METHOD_DEFLATE);
        assert_eq!(csize, 4);
        assert_eq!(usize_, data.len() as u32);
    }

    /// Stored and deflated differ ONLY in method + payload — the container code
    /// is one writer, so the headers must line up byte-for-byte up to the method
    /// field and again across the trailing EOCD shape.
    #[test]
    fn one_writer_produces_both_shapes() {
        let data = b"payload";
        let stored = build_zip_stored("n", data);
        let deflated = build_zip_entry("n", data, METHOD_DEFLATE, data);
        assert_eq!(stored[..8], deflated[..8], "headers share one writer");
        assert_eq!(read_local_header(&stored).0, METHOD_STORED);
        assert_eq!(read_local_header(&deflated).0, METHOD_DEFLATE);
        // Same payload length ⇒ same total container size.
        assert_eq!(stored.len(), deflated.len());
    }
}