vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
// RFC 1952 gzip wrapper decompression.

use crate::ir::{BufferDecl, DataType, Expr, Node, Program};
use crate::ops::compression::deflate_core;
use crate::ops::{AlgebraicLaw, OpSpec, BYTES_TO_BYTES_INPUTS, BYTES_TO_BYTES_OUTPUTS};

pub const LAWS: &[AlgebraicLaw] = &[AlgebraicLaw::Bounded { lo: 0, hi: 255 }];

pub const FHCRC: u8 = 0x02;

pub const FEXTRA: u8 = 0x04;

pub const FNAME: u8 = 0x08;

pub const FCOMMENT: u8 = 0x10;

/// Maximum decompressed bytes accepted per compressed input byte.
pub const MAX_OUTPUT_RATIO: usize = 1024;

/// gzip decompression operation.
#[derive(Debug, Clone, Copy, Default)]
pub struct GzipDecompress;

impl GzipDecompress {
    /// Declarative operation specification.
    pub const SPEC: OpSpec = OpSpec::composition(
        "compression.gzip_decompress",
        BYTES_TO_BYTES_INPUTS,
        BYTES_TO_BYTES_OUTPUTS,
        LAWS,
        Self::program,
    );

    /// Build the canonical dispatch guard program.
    #[must_use]
    pub fn program() -> Program {
        Program::new(
            vec![
                BufferDecl::read("input", 0, DataType::Bytes),
                BufferDecl::output("out", 1, DataType::Bytes),
            ],
            [1, 1, 1],
            vec![Node::if_then(
                Expr::gt(
                    Expr::buf_len("out"),
                    Expr::mul(Expr::buf_len("input"), Expr::u32(1024)),
                ),
                vec![Node::Return],
            )],
        )
    }
}

/// Decompress a gzip member with header, trailer, and bomb validation.
///
/// # Errors
///
/// Returns an actionable `Fix: ...` message when the wrapper, CRC, size,
/// stream, or expansion ratio is invalid.
pub fn decompress_bytes(input: &[u8]) -> Result<Vec<u8>, String> {
    if input.len() < 18 {
        return Err(
            "Fix: provide a complete gzip member with 10-byte header and 8-byte trailer.".into(),
        );
    }
    if input[0] != 0x1f || input[1] != 0x8b || input[2] != 8 {
        return Err("Fix: gzip header must use magic 1f8b and DEFLATE method 8.".into());
    }
    let flags = input[3];
    if flags & 0xe0 != 0 {
        return Err("Fix: gzip reserved flag bits must be zero.".into());
    }
    let declared_size = usize::try_from(u32::from_le_bytes([
        input[input.len() - 4],
        input[input.len() - 3],
        input[input.len() - 2],
        input[input.len() - 1],
    ]))
    .map_err(|error| format!("Fix: gzip ISIZE must fit usize: {error}"))?;
    let max_output = input.len().checked_mul(MAX_OUTPUT_RATIO).ok_or_else(|| {
        "Fix: reject gzip input whose max_output_ratio multiplication overflows.".to_string()
    })?;
    if declared_size > max_output {
        return Err("Fix: gzip decompression bomb from O(1) ISIZE trailer check.".into());
    }
    let start = payload_start(input, flags)?;
    let payload_end = input.len() - 8;
    if start > payload_end {
        return Err("Fix: gzip optional header fields overrun compressed payload.".into());
    }
    let output = deflate_core::decompress(&input[start..payload_end], max_output)?;
    if output.len() != declared_size {
        return Err("Fix: gzip ISIZE trailer does not match decompressed length.".into());
    }
    let expected_crc = u32::from_le_bytes([
        input[payload_end],
        input[payload_end + 1],
        input[payload_end + 2],
        input[payload_end + 3],
    ]);
    if crc32(&output) != expected_crc {
        return Err("Fix: gzip CRC-32 trailer does not match decompressed bytes.".into());
    }
    Ok(output)
}

pub fn payload_start(input: &[u8], flags: u8) -> Result<usize, String> {
    let mut pos = 10_usize;
    if flags & FEXTRA != 0 {
        let len = input
            .get(pos..pos + 2)
            .ok_or_else(|| "Fix: gzip FEXTRA length must be present before payload.".to_string())?;
        pos += 2 + usize::from(u16::from_le_bytes([len[0], len[1]]));
    }
    if flags & FNAME != 0 {
        pos = nul_terminated_end(input, pos, "FNAME")?;
    }
    if flags & FCOMMENT != 0 {
        pos = nul_terminated_end(input, pos, "FCOMMENT")?;
    }
    if flags & FHCRC != 0 {
        pos += 2;
    }
    Ok(pos)
}

pub fn nul_terminated_end(input: &[u8], start: usize, field: &str) -> Result<usize, String> {
    input[start..]
        .iter()
        .position(|&byte| byte == 0)
        .map(|offset| start + offset + 1)
        .ok_or_else(|| format!("Fix: gzip {field} must be NUL terminated."))
}

pub fn crc32(bytes: &[u8]) -> u32 {
    let mut crc = 0xffff_ffff_u32;
    for &byte in bytes {
        crc ^= u32::from(byte);
        for _ in 0..8 {
            crc = if crc & 1 != 0 {
                (crc >> 1) ^ 0xedb8_8320
            } else {
                crc >> 1
            };
        }
    }
    crc ^ 0xffff_ffff
}