use crate::{Error, Result};
use flate2::read::DeflateDecoder;
use std::io::{Read, Write};
pub const GZIP_MAGIC: [u8; 3] = [0x1F, 0x8B, 0x08];
pub const DEFAULT_MAX_INFLATE_BYTES: usize = 256 * 1024 * 1024;
const INITIAL_INFLATE_CAPACITY_BYTES: usize = 1024 * 1024;
#[derive(Debug, Clone, Copy)]
pub struct InflateLimits {
pub max_output_bytes: usize,
}
impl Default for InflateLimits {
fn default() -> Self {
Self {
max_output_bytes: DEFAULT_MAX_INFLATE_BYTES,
}
}
}
pub const REVIT_STORED_PAGE_BYTES: usize = 65_249;
pub const REVIT_PAGE_PAYLOAD_BYTES: usize = 64_896;
pub const REVIT_PAGE_CHECKSUM_BYTES: usize = REVIT_STORED_PAGE_BYTES - REVIT_PAGE_PAYLOAD_BYTES;
fn clean_stream_path(path: &str) -> &str {
let clean = path
.trim_start_matches('/')
.trim_start_matches("Root Entry/");
clean.trim_start_matches('/')
}
pub fn is_checksum_paged_stream(path: &str) -> bool {
let clean = clean_stream_path(path);
if let Some(rest) = clean
.strip_prefix("Partitions/")
.or_else(|| clean.strip_prefix("partitions/"))
{
return !rest.is_empty() && !rest.contains('/');
}
matches!(
clean,
"Formats/Latest"
| "Global/ContentDocuments"
| "Global/DocumentIncrementTable"
| "Global/ElemTable"
| "Global/History"
| "Global/Latest"
| "Global/PartitionTable"
)
}
pub fn is_revit_paged_loader_candidate(path: &str) -> bool {
let clean = clean_stream_path(path);
if clean.eq_ignore_ascii_case("Formats/Latest") {
return true;
}
is_checksum_paged_stream(path)
}
pub fn strip_revit_page_checksums(data: &[u8]) -> Vec<u8> {
let full_pages = data.len() / REVIT_STORED_PAGE_BYTES;
if full_pages == 0 {
return data.to_vec();
}
let remainder = data.len() - full_pages * REVIT_STORED_PAGE_BYTES;
let mut out = Vec::with_capacity(full_pages * REVIT_PAGE_PAYLOAD_BYTES + remainder);
for page in 0..full_pages {
let start = page * REVIT_STORED_PAGE_BYTES;
out.extend_from_slice(&data[start..start + REVIT_PAGE_PAYLOAD_BYTES]);
}
out.extend_from_slice(&data[full_pages * REVIT_STORED_PAGE_BYTES..]);
out
}
pub fn prepare_stream_for_inflate<'a>(
stream_name: &str,
stored: &'a [u8],
) -> std::borrow::Cow<'a, [u8]> {
if is_checksum_paged_stream(stream_name) {
std::borrow::Cow::Owned(strip_revit_page_checksums(stored))
} else {
std::borrow::Cow::Borrowed(stored)
}
}
pub fn inflate_stream_at(stream_name: &str, stored: &[u8], offset: usize) -> Result<Vec<u8>> {
let prepared = prepare_stream_for_inflate(stream_name, stored);
inflate_at(prepared.as_ref(), offset)
}
pub fn inflate_stream_auto(stream_name: &str, stored: &[u8]) -> Result<(usize, Vec<u8>)> {
let prepared = prepare_stream_for_inflate(stream_name, stored);
inflate_at_auto(prepared.as_ref())
}
pub fn inflate_all_chunks_for_stream(stream_name: &str, stored: &[u8]) -> Vec<Vec<u8>> {
let prepared = prepare_stream_for_inflate(stream_name, stored);
inflate_all_chunks(prepared.as_ref())
}
#[derive(Debug, Default)]
pub struct InflatedStream {
concat: Vec<u8>,
chunk_ends: Vec<usize>,
}
impl InflatedStream {
pub fn from_stored(stream_name: &str, stored: &[u8]) -> Self {
let chunks = inflate_all_chunks_for_stream(stream_name, stored);
let total: usize = chunks.iter().map(|chunk| chunk.len()).sum();
let mut concat = Vec::with_capacity(total);
let mut chunk_ends = Vec::with_capacity(chunks.len());
for chunk in &chunks {
concat.extend_from_slice(chunk);
chunk_ends.push(concat.len());
}
Self { concat, chunk_ends }
}
pub fn bytes(&self) -> &[u8] {
&self.concat
}
pub fn chunk_ends(&self) -> &[usize] {
&self.chunk_ends
}
pub fn chunk_count(&self) -> usize {
self.chunk_ends.len()
}
pub fn chunks(&self) -> impl Iterator<Item = &[u8]> {
let mut start = 0usize;
self.chunk_ends.iter().map(move |end| {
let slice = &self.concat[start..*end];
start = *end;
slice
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChecksumTailStripping {
Disabled,
Enabled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FormatsIntegrityStatus {
Ok,
Uncertain,
Incomplete,
}
pub const RVT_FORMATS_MULTIPAGE_UNVERIFIED: &str = "RVT_FORMATS_MULTIPAGE_UNVERIFIED";
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FormatsLatestIntegrity {
pub stream: String,
pub stored_bytes: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub inflated_bytes: Option<usize>,
pub page_boundary_detected: bool,
pub checksum_tail_stripping: ChecksumTailStripping,
pub integrity_status: FormatsIntegrityStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostic_code: Option<String>,
#[serde(default)]
pub schema_scan_truncated: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema_scanned_bytes: Option<usize>,
}
impl FormatsLatestIntegrity {
pub fn summary_line(&self) -> String {
let mut line = match self.integrity_status {
FormatsIntegrityStatus::Ok => format!(
"Formats/Latest · {} stored · inflate ok · single-page",
self.stored_bytes
),
FormatsIntegrityStatus::Uncertain => format!(
"Formats/Latest · {} stored · multipage integrity uncertain (strip disabled)",
self.stored_bytes
),
FormatsIntegrityStatus::Incomplete => format!(
"Formats/Latest · {} stored · inflate incomplete",
self.stored_bytes
),
};
if self.schema_scan_truncated {
let scanned = self
.schema_scanned_bytes
.unwrap_or(crate::formats::SCHEMA_SCAN_LIMIT);
let total = self.inflated_bytes.unwrap_or(scanned);
line.push_str(&format!(
" · schema scan truncated ({scanned} of {total} inflated bytes scanned)"
));
}
line
}
}
pub fn diagnose_formats_latest_integrity(stored: &[u8]) -> FormatsLatestIntegrity {
let stored_bytes = stored.len();
let page_boundary_detected = stored_bytes >= REVIT_STORED_PAGE_BYTES;
let inflate = inflate_stream_at(crate::streams::FORMATS_LATEST, stored, 0);
let inflated_bytes = inflate.as_ref().ok().map(|b| b.len());
let (integrity_status, diagnostic_code) = match &inflate {
Err(_) => (FormatsIntegrityStatus::Incomplete, None),
Ok(_)
if gzip_trailer_verifies(&prepare_stream_for_inflate(
crate::streams::FORMATS_LATEST,
stored,
)) =>
{
(FormatsIntegrityStatus::Ok, None)
}
Ok(_) => (
FormatsIntegrityStatus::Uncertain,
Some(RVT_FORMATS_MULTIPAGE_UNVERIFIED.to_string()),
),
};
let schema_scanned_bytes = inflated_bytes;
let schema_scan_truncated = false;
FormatsLatestIntegrity {
stream: crate::streams::FORMATS_LATEST.to_string(),
stored_bytes,
inflated_bytes,
page_boundary_detected,
checksum_tail_stripping: ChecksumTailStripping::Enabled,
integrity_status,
diagnostic_code,
schema_scan_truncated,
schema_scanned_bytes,
}
}
fn gzip_trailer_verifies(data: &[u8]) -> bool {
let mut decoder = flate2::read::GzDecoder::new(data);
let mut buf = [0u8; 8192];
let mut total = 0usize;
loop {
match std::io::Read::read(&mut decoder, &mut buf) {
Ok(0) => return true,
Ok(n) => {
total = total.saturating_add(n);
if total > DEFAULT_MAX_INFLATE_BYTES {
return false;
}
}
Err(_) => return false,
}
}
}
pub fn has_gzip_magic(data: &[u8], offset: usize) -> bool {
offset
.checked_add(GZIP_MAGIC.len())
.and_then(|end| data.get(offset..end))
== Some(GZIP_MAGIC.as_slice())
}
pub fn gzip_header_len(data: &[u8], offset: usize) -> Option<usize> {
if !has_gzip_magic(data, offset) {
return None;
}
let flags = *data.get(offset.checked_add(3)?)?;
let mut pos = offset.checked_add(10)?;
if flags & 0x04 != 0 {
let xlen_end = pos.checked_add(2)?;
let xlen_bytes = data.get(pos..xlen_end)?;
let xlen = u16::from_le_bytes([xlen_bytes[0], xlen_bytes[1]]) as usize;
pos = xlen_end.checked_add(xlen)?;
}
if flags & 0x08 != 0 {
pos = data
.get(pos..)?
.iter()
.position(|&b| b == 0)
.and_then(|i| pos.checked_add(i)?.checked_add(1))?;
}
if flags & 0x10 != 0 {
pos = data
.get(pos..)?
.iter()
.position(|&b| b == 0)
.and_then(|i| pos.checked_add(i)?.checked_add(1))?;
}
if flags & 0x02 != 0 {
pos = pos.checked_add(2)?;
}
if pos > data.len() {
return None;
}
Some(pos - offset)
}
pub fn inflate_at_with_limits(
data: &[u8],
offset: usize,
limits: InflateLimits,
) -> Result<Vec<u8>> {
let header_len =
gzip_header_len(data, offset).ok_or_else(|| Error::Decompress("no gzip header".into()))?;
let body_start = offset
.checked_add(header_len)
.ok_or_else(|| Error::Decompress("gzip header offset overflow".into()))?;
let body = data
.get(body_start..)
.ok_or_else(|| Error::Decompress("gzip header extends past input".into()))?;
let cap = body
.len()
.saturating_mul(4)
.min(INITIAL_INFLATE_CAPACITY_BYTES)
.min(limits.max_output_bytes);
let mut out = Vec::with_capacity(cap);
let mut decoder = DeflateDecoder::new(body);
let mut buf = [0u8; 8192];
loop {
let n = decoder
.read(&mut buf)
.map_err(|e| Error::Decompress(format!("DEFLATE at offset {offset}: {e}")))?;
if n == 0 {
break;
}
let next_len = out.len().checked_add(n).ok_or_else(|| {
Error::DecompressLimitExceeded("DEFLATE output length overflow".into())
})?;
if next_len > limits.max_output_bytes {
return Err(Error::DecompressLimitExceeded(format!(
"DEFLATE at offset {offset} would exceed {} bytes",
limits.max_output_bytes
)));
}
out.extend_from_slice(&buf[..n]);
}
out.shrink_to_fit();
Ok(out)
}
pub fn inflate_at(data: &[u8], offset: usize) -> Result<Vec<u8>> {
inflate_at_with_limits(data, offset, InflateLimits::default())
}
pub fn inflate_at_auto(data: &[u8]) -> Result<(usize, Vec<u8>)> {
let offset = find_gzip_offsets(data).into_iter().next().unwrap_or(8);
let out = inflate_at_with_limits(data, offset, InflateLimits::default())?;
Ok((offset, out))
}
pub fn find_gzip_offsets(data: &[u8]) -> Vec<usize> {
let mut hits = Vec::new();
let mut i = 0;
while i + 3 <= data.len() {
if data[i..i + 3] == GZIP_MAGIC {
hits.push(i);
i += 3;
} else {
i += 1;
}
}
hits
}
pub fn inflate_all_chunks_with_limits(
data: &[u8],
per_chunk: InflateLimits,
aggregate: usize,
) -> Vec<Vec<u8>> {
let mut total: usize = 0;
let mut results = Vec::new();
for off in find_gzip_offsets(data) {
let chunk = match inflate_at_with_limits(data, off, per_chunk) {
Ok(c) => c,
Err(_) => continue,
};
if total.saturating_add(chunk.len()) > aggregate {
break;
}
total += chunk.len();
results.push(chunk);
}
results
}
pub fn inflate_all_chunks(data: &[u8]) -> Vec<Vec<u8>> {
inflate_all_chunks_with_limits(
data,
InflateLimits::default(),
1024 * 1024 * 1024, )
}
pub fn truncated_gzip_encode(bytes: &[u8]) -> Result<Vec<u8>> {
use flate2::{Compression, write::DeflateEncoder};
let mut out = Vec::with_capacity(bytes.len());
out.extend_from_slice(&[0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]);
let mut enc = DeflateEncoder::new(&mut out, Compression::default());
enc.write_all(bytes)
.map_err(|e| Error::Decompress(format!("deflate write: {e}")))?;
enc.finish()
.map_err(|e| Error::Decompress(format!("deflate finish: {e}")))?;
Ok(out)
}
pub fn truncated_gzip_encode_with_prefix8(bytes: &[u8]) -> Result<Vec<u8>> {
let body = truncated_gzip_encode(bytes)?;
let mut out = Vec::with_capacity(8 + body.len());
out.extend_from_slice(&[0u8; 8]);
out.extend_from_slice(&body);
Ok(out)
}
pub fn validate_truncated_gzip_round_trip(bytes: &[u8]) -> Result<()> {
let encoded = truncated_gzip_encode(bytes)?;
let decoded = inflate_at(&encoded, 0)?;
if decoded != bytes {
let diff = decoded
.iter()
.zip(bytes.iter())
.position(|(a, b)| a != b)
.unwrap_or(decoded.len().min(bytes.len()));
return Err(Error::Decompress(format!(
"truncated_gzip round-trip mismatch at offset {diff}: encoded {} bytes, inflated to \
{} bytes, expected {} bytes",
encoded.len(),
decoded.len(),
bytes.len()
)));
}
Ok(())
}
pub fn validate_truncated_gzip_prefix8_round_trip(bytes: &[u8]) -> Result<()> {
let encoded = truncated_gzip_encode_with_prefix8(bytes)?;
if encoded.len() < 8 || encoded[..8] != [0u8; 8] {
return Err(Error::Decompress(
"prefix8 encode: first 8 bytes not zero — framing invariant broken".into(),
));
}
let decoded = inflate_at(&encoded, 8)?;
if decoded != bytes {
let diff = decoded
.iter()
.zip(bytes.iter())
.position(|(a, b)| a != b)
.unwrap_or(decoded.len().min(bytes.len()));
return Err(Error::Decompress(format!(
"prefix8 round-trip mismatch at offset {diff}: encoded {} bytes (incl. 8-byte \
prefix), inflated to {} bytes, expected {} bytes",
encoded.len(),
decoded.len(),
bytes.len()
)));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_magic() {
assert!(has_gzip_magic(&[0x1F, 0x8B, 0x08, 0, 0, 0], 0));
assert!(!has_gzip_magic(&[0x1F, 0x8B, 0x07], 0));
assert!(has_gzip_magic(&[0xFF, 0x1F, 0x8B, 0x08], 1));
}
#[test]
fn minimal_gzip_header_len() {
let hdr = [0x1F, 0x8B, 0x08, 0x00, 0, 0, 0, 0, 0, 0x0B];
assert_eq!(gzip_header_len(&hdr, 0), Some(10));
}
#[test]
fn gzip_header_with_fname() {
let hdr = [
0x1F, 0x8B, 0x08, 0x08, 0, 0, 0, 0, 0, 0x0B, b'f', b'o', b'o', 0,
];
assert_eq!(gzip_header_len(&hdr, 0), Some(14));
}
#[test]
fn inflate_capacity_hint_does_not_scale_with_remaining_input() {
let mut data = truncated_gzip_encode(b"tiny payload").unwrap();
data.resize(data.len() + 8 * 1024 * 1024, 0);
let out = inflate_at_with_limits(&data, 0, InflateLimits::default()).unwrap();
assert_eq!(out, b"tiny payload");
assert!(
out.capacity() <= 1024 * 1024,
"capacity {} scales with the 8 MiB of trailing input",
out.capacity()
);
}
#[test]
fn compressed_bomb_rejected_by_inflate_limits() {
use flate2::{Compression, write::DeflateEncoder};
use std::io::Write;
let payload = vec![0u8; 1024 * 1024]; let mut bomb = Vec::new();
bomb.extend_from_slice(&[0x1f, 0x8b, 0x08, 0x00, 0, 0, 0, 0, 0, 0xff]);
let mut enc = DeflateEncoder::new(&mut bomb, Compression::default());
enc.write_all(&payload).unwrap();
enc.finish().unwrap();
let tight = InflateLimits {
max_output_bytes: 64 * 1024,
};
let result = inflate_at_with_limits(&bomb, 0, tight);
match result {
Err(Error::DecompressLimitExceeded(msg)) => {
assert!(msg.contains("65536"), "error should name the cap: {msg}");
}
other => panic!("expected DecompressLimitExceeded, got {other:?}"),
}
}
#[test]
fn legitimate_decompression_under_default_limit_still_works() {
let payload = b"hello, world";
let compressed = truncated_gzip_encode(payload).unwrap();
let out = inflate_at_with_limits(&compressed, 0, InflateLimits::default()).unwrap();
assert_eq!(&out[..], payload);
}
#[test]
fn round_trip_empty_payload_is_ok() {
assert!(validate_truncated_gzip_round_trip(b"").is_ok());
assert!(validate_truncated_gzip_prefix8_round_trip(b"").is_ok());
}
#[test]
fn round_trip_single_byte_is_ok() {
for b in 0..=255_u8 {
let payload = [b];
validate_truncated_gzip_round_trip(&payload)
.unwrap_or_else(|e| panic!("round-trip failed for 0x{b:02x}: {e}"));
}
}
#[test]
fn round_trip_typical_schema_payload_is_ok() {
let mut payload = Vec::with_capacity(8 * 1024);
for i in 0..8 * 1024 {
payload.push(((i * 17 + 3) % 256) as u8);
}
validate_truncated_gzip_round_trip(&payload).unwrap();
}
#[test]
fn round_trip_large_payload_crosses_32k_deflate_window() {
let payload: Vec<u8> = (0..128 * 1024).map(|i| (i as u8).wrapping_mul(7)).collect();
validate_truncated_gzip_round_trip(&payload).unwrap();
}
#[test]
fn round_trip_highly_compressible_payload() {
let payload = vec![0u8; 32 * 1024];
validate_truncated_gzip_round_trip(&payload).unwrap();
}
#[test]
fn round_trip_highly_incompressible_payload() {
let mut payload = Vec::with_capacity(1024);
let mut state = 0x12345_u32;
for _ in 0..1024 {
state = state.wrapping_mul(1103515245).wrapping_add(12345);
payload.push((state >> 16) as u8);
}
validate_truncated_gzip_round_trip(&payload).unwrap();
}
#[test]
fn prefix8_encoder_zero_fills_first_eight_bytes() {
let encoded = truncated_gzip_encode_with_prefix8(b"test").unwrap();
assert!(encoded.len() > 8);
assert_eq!(&encoded[..8], &[0u8; 8]);
assert_eq!(encoded[8], 0x1f);
assert_eq!(encoded[9], 0x8b);
}
#[test]
fn prefix8_round_trip_includes_framing_invariant_check() {
for payload in [
&b""[..],
&b"a"[..],
&b"Global/Latest typical chunk"[..],
&vec![0xFFu8; 4096][..],
] {
validate_truncated_gzip_prefix8_round_trip(payload).unwrap();
}
}
#[test]
fn encoded_header_is_canonical_ten_bytes() {
let encoded = truncated_gzip_encode(b"x").unwrap();
assert!(encoded.len() > 10);
assert_eq!(
&encoded[..10],
&[0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
);
}
#[test]
fn checksum_paged_stream_paths() {
assert!(is_checksum_paged_stream("Formats/Latest"));
assert!(is_checksum_paged_stream("/Formats/Latest"));
assert!(is_revit_paged_loader_candidate("Formats/Latest"));
assert!(is_checksum_paged_stream("Global/Latest"));
assert!(is_checksum_paged_stream("Global/ElemTable"));
assert!(is_checksum_paged_stream("Partitions/67"));
assert!(is_checksum_paged_stream("Partitions/46"));
assert!(is_checksum_paged_stream("/Partitions/46"));
assert!(!is_checksum_paged_stream("BasicFileInfo"));
assert!(!is_checksum_paged_stream("PartAtom"));
assert!(!is_checksum_paged_stream("ProjectInformation"));
assert!(!is_checksum_paged_stream("RevitPreview4.0"));
}
#[test]
fn strip_revit_page_checksums_cuts_full_page_trailers() {
let mut stored = vec![0xABu8; REVIT_PAGE_PAYLOAD_BYTES];
stored.extend(vec![0xCDu8; REVIT_PAGE_CHECKSUM_BYTES]);
stored.extend(vec![0xEFu8; 100]); let clean = strip_revit_page_checksums(&stored);
assert_eq!(clean.len(), REVIT_PAGE_PAYLOAD_BYTES + 100);
assert!(
clean
.iter()
.take(REVIT_PAGE_PAYLOAD_BYTES)
.all(|&b| b == 0xAB)
);
assert!(
clean
.iter()
.skip(REVIT_PAGE_PAYLOAD_BYTES)
.all(|&b| b == 0xEF)
);
assert!(!clean.contains(&0xCD));
}
#[test]
fn strip_under_one_page_is_identity() {
let small = vec![1u8, 2, 3, 4, 5];
assert_eq!(strip_revit_page_checksums(&small), small);
}
#[test]
fn strip_exact_page_boundary_drops_only_trailer() {
let mut stored = vec![0x11u8; REVIT_PAGE_PAYLOAD_BYTES];
stored.extend(vec![0x22u8; REVIT_PAGE_CHECKSUM_BYTES]);
assert_eq!(stored.len(), REVIT_STORED_PAGE_BYTES);
let clean = strip_revit_page_checksums(&stored);
assert_eq!(clean, vec![0x11u8; REVIT_PAGE_PAYLOAD_BYTES]);
}
#[test]
fn strip_empty_is_empty() {
assert!(strip_revit_page_checksums(&[]).is_empty());
}
#[test]
fn strip_two_full_pages_plus_partial() {
let mut stored = Vec::new();
for page in 0..2 {
stored.extend(vec![page as u8; REVIT_PAGE_PAYLOAD_BYTES]);
stored.extend(vec![0xFFu8; REVIT_PAGE_CHECKSUM_BYTES]);
}
stored.extend(vec![0x33u8; 50]);
let clean = strip_revit_page_checksums(&stored);
assert_eq!(clean.len(), REVIT_PAGE_PAYLOAD_BYTES * 2 + 50);
assert!(clean[..REVIT_PAGE_PAYLOAD_BYTES].iter().all(|&b| b == 0));
assert!(
clean[REVIT_PAGE_PAYLOAD_BYTES..REVIT_PAGE_PAYLOAD_BYTES * 2]
.iter()
.all(|&b| b == 1)
);
assert!(
clean[REVIT_PAGE_PAYLOAD_BYTES * 2..]
.iter()
.all(|&b| b == 0x33)
);
assert!(!clean.contains(&0xFF));
}
#[test]
fn synthetic_multipage_partitions_require_strip_before_inflate() {
let mut state = 0xC0FFEE_u32;
let payload: Vec<u8> = (0..180_000)
.map(|_| {
state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
(state >> 16) as u8
})
.collect();
let gzip = truncated_gzip_encode(&payload).unwrap();
assert!(
gzip.len() > REVIT_PAGE_PAYLOAD_BYTES,
"compressed fixture too small: {}",
gzip.len()
);
let paged = inject_page_checksums(&gzip);
assert!(
paged.len() >= REVIT_STORED_PAGE_BYTES,
"fixture must cross a page boundary"
);
assert_ne!(paged, gzip);
if let Ok(naive) = inflate_at(&paged, 0) {
assert_ne!(
naive, payload,
"paged bytes without strip must not decode to the original payload"
);
}
let recovered = inflate_stream_at("Partitions/46", &paged, 0).expect("strip+inflate");
assert_eq!(recovered, payload);
assert_eq!(
inflate_stream_at("Global/Latest", &paged, 0).unwrap(),
payload
);
assert_eq!(
inflate_stream_at("Formats/Latest", &paged, 0).unwrap(),
payload
);
let small = truncated_gzip_encode(b"tiny").unwrap();
assert!(small.len() < REVIT_STORED_PAGE_BYTES);
assert_eq!(
inflate_stream_at("Partitions/1", &small, 0).unwrap(),
b"tiny"
);
assert_eq!(
inflate_stream_at("BasicFileInfo", &paged, 0)
.err()
.map(|e| e.to_string()),
inflate_at(&paged, 0).err().map(|e| e.to_string()),
"non-paged paths must not strip"
);
}
fn inject_page_checksums(payload: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(
payload.len() + (payload.len() / REVIT_PAGE_PAYLOAD_BYTES) * REVIT_PAGE_CHECKSUM_BYTES,
);
let mut offset = 0;
while offset < payload.len() {
let end = (offset + REVIT_PAGE_PAYLOAD_BYTES).min(payload.len());
out.extend_from_slice(&payload[offset..end]);
if end - offset == REVIT_PAGE_PAYLOAD_BYTES {
out.extend(std::iter::repeat_n(0x5A, REVIT_PAGE_CHECKSUM_BYTES));
}
offset = end;
}
out
}
fn gzip_with_trailer(payload: &[u8]) -> Vec<u8> {
let mut gzip = truncated_gzip_encode(payload).unwrap();
let mut crc = flate2::Crc::new();
crc.update(payload);
gzip.extend_from_slice(&crc.sum().to_le_bytes());
gzip.extend_from_slice(&(payload.len() as u32).to_le_bytes());
gzip
}
#[test]
fn formats_integrity_single_page_ok() {
let gzip = gzip_with_trailer(b"formats-single-page");
assert!(gzip.len() < REVIT_STORED_PAGE_BYTES);
let diag = diagnose_formats_latest_integrity(&gzip);
assert_eq!(diag.stream, "Formats/Latest");
assert_eq!(diag.inflated_bytes, Some(b"formats-single-page".len()));
assert!(!diag.page_boundary_detected);
assert_eq!(diag.checksum_tail_stripping, ChecksumTailStripping::Enabled);
assert_eq!(diag.integrity_status, FormatsIntegrityStatus::Ok);
assert!(diag.diagnostic_code.is_none());
assert!(!diag.schema_scan_truncated);
let bare = diagnose_formats_latest_integrity(&truncated_gzip_encode(b"x").unwrap());
assert_eq!(bare.integrity_status, FormatsIntegrityStatus::Uncertain);
assert_eq!(
bare.diagnostic_code.as_deref(),
Some(RVT_FORMATS_MULTIPAGE_UNVERIFIED)
);
}
#[test]
fn formats_multipage_inflates_to_its_exact_payload() {
let mut state = 0xC0FFEE_u32;
let payload: Vec<u8> = (0..180_000)
.map(|_| {
state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
(state >> 16) as u8
})
.collect();
let gzip = gzip_with_trailer(&payload);
let paged = inject_page_checksums(&gzip);
assert!(
paged.len() >= REVIT_STORED_PAGE_BYTES,
"fixture must cross a page boundary"
);
assert!(is_checksum_paged_stream("Formats/Latest"));
assert_eq!(
inflate_stream_at("Formats/Latest", &paged, 0).unwrap(),
payload
);
let diag = diagnose_formats_latest_integrity(&paged);
assert!(diag.page_boundary_detected);
assert_eq!(diag.inflated_bytes, Some(payload.len()));
assert_eq!(diag.integrity_status, FormatsIntegrityStatus::Ok);
assert!(diag.diagnostic_code.is_none());
}
#[test]
fn formats_integrity_garbage_under_one_page_is_incomplete() {
let garbage = vec![0u8; 64];
let diag = diagnose_formats_latest_integrity(&garbage);
assert!(!diag.page_boundary_detected);
assert_eq!(diag.integrity_status, FormatsIntegrityStatus::Incomplete);
assert!(diag.inflated_bytes.is_none());
assert!(diag.schema_scanned_bytes.is_none());
}
}