use std::io::Read;
pub(crate) const MAX_INFLATE_BYTES: u64 = 16 * 1024 * 1024;
#[must_use]
fn is_gzip_magic(bytes: &[u8]) -> bool {
bytes.len() >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b
}
#[must_use]
fn is_zlib_magic(bytes: &[u8]) -> bool {
if bytes.len() < 2 {
return false;
}
let cmf = bytes[0];
let flg = bytes[1];
(cmf & 0x0f) == 8 && (cmf >> 4) <= 7 && (u16::from(cmf) * 256 + u16::from(flg)) % 31 == 0
}
pub(crate) fn has_container_magic(bytes: &[u8]) -> bool {
is_gzip_magic(bytes) || is_zlib_magic(bytes)
}
#[must_use]
pub(crate) fn try_inflate_to_text(bytes: &[u8]) -> Option<String> {
let mut out = Vec::with_capacity(bytes.len());
let inflate_result = if is_gzip_magic(bytes) {
flate2::read::GzDecoder::new(bytes)
.take(MAX_INFLATE_BYTES)
.read_to_end(&mut out)
.map(|_| "gzip")
} else if is_zlib_magic(bytes) {
flate2::read::ZlibDecoder::new(bytes)
.take(MAX_INFLATE_BYTES)
.read_to_end(&mut out)
.map(|_| "zlib")
} else {
return None;
};
let container = match inflate_result {
Ok(container) => container,
Err(error) if !out.is_empty() => {
crate::telemetry::record_decode_truncation();
tracing::warn!(
compressed_bytes = bytes.len(),
inflated_prefix = out.len(),
%error,
"compressed decode truncated or failed mid-stream; rescanning inflated prefix"
);
"truncated"
}
Err(error) => {
tracing::warn!(
compressed_bytes = bytes.len(),
%error,
"compressed decode failed; original encoded bytes remain in the scan"
);
return None;
}
};
let text = match String::from_utf8(out) {
Ok(text) => text,
Err(error) => {
tracing::warn!(
container,
inflated_bytes = error.as_bytes().len(),
"compressed decode produced non-UTF-8 bytes; original encoded bytes remain in the scan"
);
return None;
}
};
if text.is_empty() {
return None;
}
Some(text)
}