From 062270da50a411cfd811c752dc935e9be7162552 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo Date: Thu, 15 Oct 2026 21:23:28 +0200 Subject: [PATCH] fix(cos): fill xref gaps on the compact cross-reference index (refs #1628) main now keeps valid tables as a compact index; the gap filling reads entries through it and materializes them only when it repairs. --- crates/cos/src/document.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/cos/src/document.rs b/crates/cos/src/document.rs index e9818454bd..e102993aec 100644 --- a/crates/cos/src/document.rs +++ b/crates/cos/src/document.rs @@ -268,6 +268,20 @@ impl XrefIndex { } } + /// Every entry, newest section first, as the materialized reader keeps them. + fn to_map(&self, file: &[u8]) -> BTreeMap { + match self { + Self::Materialized(entries) => entries.as_ref().clone(), + Self::Compact { revisions, .. } => { + let mut numbers = Vec::new(); + for revision in revisions { + revision.extend_numbers(&mut numbers); + } + numbers.into_iter().filter_map(|num| self.lookup(num, file).map(|entry| (num, entry))).collect() + } + } + } + fn max_num(&self) -> u32 { match self { Self::Compact { max_num, .. } => *max_num, @@ -1924,8 +1938,8 @@ impl Document { /// Existing entries are never replaced: an object freed by an update stays freed. fn fill_xref_gaps(&mut self, log: &mut Vec) { let entries = &self.entries; - let shifted = matches!(entries.get(&0), Some(XrefEntry::InFile { .. })); - let set_aside = self.xref_oddities.iter().any(|(_, num)| !entries.contains_key(num)); + let shifted = matches!(entries.lookup(0, &self.data), Some(XrefEntry::InFile { .. })); + let set_aside = self.xref_oddities.iter().any(|(_, num)| entries.lookup(*num, &self.data).is_none()); if !(shifted || set_aside) { return; } @@ -1933,7 +1947,7 @@ impl Document { for (num, offset, generation) in object_headers(&self.data) { located.insert(num, (offset, generation)); } - let mut filled = entries.as_ref().clone(); + let mut filled = entries.to_map(&self.data); let mut found = 0usize; for (&num, &(offset, generation)) in &located { if num != 0 && !filled.contains_key(&num) { @@ -1943,7 +1957,7 @@ impl Document { } if found > 0 { log.push(format!("the cross-reference data leaves out {found} objects the file contains; located them by their object headers")); - self.entries = Arc::new(filled); + self.entries = Arc::new(XrefIndex::Materialized(Arc::new(filled))); } self.located = Arc::new(located); }