From 733bfcf6fa2e17dbc94487fb1b9eb4f896e53845 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 18 Jul 2026 15:15:29 +0400 Subject: perf(assets): reuse archives during mission loads --- crates/fparkan-resource/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'crates/fparkan-resource') diff --git a/crates/fparkan-resource/src/lib.rs b/crates/fparkan-resource/src/lib.rs index 8fbd62e..f996a71 100644 --- a/crates/fparkan-resource/src/lib.rs +++ b/crates/fparkan-resource/src/lib.rs @@ -258,6 +258,21 @@ pub trait ResourceRepository { /// Returns [`ResourceError`] when the archive is missing, unsupported, or /// malformed. fn open_archive(&self, path: &NormalizedPath) -> Result; + /// Opens an archive known to remain unchanged for the caller's bounded + /// loading transaction. + /// + /// Implementations may reuse an already decoded archive without a second + /// content-fingerprint pass. Callers must use this only while their asset + /// source is immutable; the default preserves [`Self::open_archive`]'s + /// strict invalidation behavior. + /// + /// # Errors + /// + /// Returns [`ResourceError`] when the archive is missing, unsupported, or + /// malformed. + fn open_archive_unchanged(&self, path: &NormalizedPath) -> Result { + self.open_archive(path) + } /// Finds entry. /// /// # Errors @@ -524,6 +539,19 @@ impl ResourceRepository for CachedResourceRepository { } } + fn open_archive_unchanged(&self, path: &NormalizedPath) -> Result { + let key = path.identity_bytes().to_vec(); + let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; + if let Some(id) = state.paths.get(&key).copied() { + if state.archive(id)?.document.is_some() { + state.touch_archive(id)?; + return Ok(id); + } + } + drop(state); + self.open_archive(path) + } + fn find( &self, archive: ArchiveId, @@ -1101,6 +1129,23 @@ mod tests { assert_eq!(vfs.metadata_reads.load(Ordering::Relaxed), 1); } + #[test] + fn unchanged_transaction_reuses_decoded_archive_without_rereading() { + let path = archive_path(b"archives/test.lib").expect("path"); + let bytes = Arc::from(build_nres(&[("one", b"payload")]).into_boxed_slice()); + let vfs = Arc::new(CountingVfs::new(bytes)); + let repo = CachedResourceRepository::new(Arc::clone(&vfs) as Arc); + + let first = repo.open_archive_unchanged(&path).expect("first open"); + let second = repo + .open_archive_unchanged(&path) + .expect("transactional cached open"); + + assert_eq!(first, second); + assert_eq!(vfs.reads.load(Ordering::Relaxed), 1); + assert_eq!(vfs.metadata_reads.load(Ordering::Relaxed), 1); + } + #[test] fn concurrent_same_archive_open_reuses_archive_id() { let path = archive_path(b"archives/test.lib").expect("path"); -- cgit v1.2.3