fix: decode payloads outside resource lock

This commit is contained in:
2026-06-22 16:36:50 +04:00
parent d579b696e6
commit fb97405e0c
+38 -17
View File
@@ -210,7 +210,7 @@ struct ArchiveSlot {
fingerprint: Sha256Digest, fingerprint: Sha256Digest,
generation: u64, generation: u64,
kind: ArchiveKind, kind: ArchiveKind,
document: ArchiveDocument, document: Arc<ArchiveDocument>,
} }
enum ArchiveDocument { enum ArchiveDocument {
@@ -218,6 +218,11 @@ enum ArchiveDocument {
Rsli(fparkan_rsli::RsliDocument), Rsli(fparkan_rsli::RsliDocument),
} }
struct PayloadDecodeTask {
document: Arc<ArchiveDocument>,
key: ResourceKey,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct DecodedPayloadCache { struct DecodedPayloadCache {
max_entries: usize, max_entries: usize,
@@ -320,7 +325,7 @@ impl ResourceRepository for CachedResourceRepository {
) -> Result<Option<EntryHandle>, ResourceError> { ) -> Result<Option<EntryHandle>, ResourceError> {
let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?;
let slot = state.archive(archive)?; let slot = state.archive(archive)?;
let local = match &slot.document { let local = match slot.document.as_ref() {
ArchiveDocument::Nres(document) => document.find_bytes(&name.0).map(|id| id.0), ArchiveDocument::Nres(document) => document.find_bytes(&name.0).map(|id| id.0),
ArchiveDocument::Rsli(document) => document.find_bytes(&name.0).map(|id| id.0), ArchiveDocument::Rsli(document) => document.find_bytes(&name.0).map(|id| id.0),
}; };
@@ -334,7 +339,7 @@ impl ResourceRepository for CachedResourceRepository {
fn first_entry(&self, archive: ArchiveId) -> Result<Option<EntryHandle>, ResourceError> { fn first_entry(&self, archive: ArchiveId) -> Result<Option<EntryHandle>, ResourceError> {
let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?;
let slot = state.archive(archive)?; let slot = state.archive(archive)?;
let local = match &slot.document { let local = match slot.document.as_ref() {
ArchiveDocument::Nres(document) => document.entries().first().map(|entry| entry.id().0), ArchiveDocument::Nres(document) => document.entries().first().map(|entry| entry.id().0),
ArchiveDocument::Rsli(document) => document.entry(fparkan_rsli::EntryId(0)).map(|_| 0), ArchiveDocument::Rsli(document) => document.entry(fparkan_rsli::EntryId(0)).map(|_| 0),
}; };
@@ -346,21 +351,27 @@ impl ResourceRepository for CachedResourceRepository {
} }
fn read(&self, entry: EntryHandle) -> Result<ResourceBytes, ResourceError> { fn read(&self, entry: EntryHandle) -> Result<ResourceBytes, ResourceError> {
let task = {
let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?;
if let Some(bytes) = state.payload_cache.get(entry) { if let Some(bytes) = state.payload_cache.get(entry) {
return Ok(ResourceBytes::Shared(bytes)); return Ok(ResourceBytes::Shared(bytes));
} }
state.payload_decode_task(entry)?
let payload = {
let slot = state.entry_archive(entry)?;
let key = slot.entry_key(entry.local)?;
slot.read_payload(entry.local)
.map_err(|source| ResourceError::EntryRead {
key: key.clone(),
source,
})?
}; };
let payload =
task.document
.read_payload(entry.local)
.map_err(|source| ResourceError::EntryRead {
key: task.key,
source,
})?;
let shared = Arc::from(payload.into_boxed_slice()); let shared = Arc::from(payload.into_boxed_slice());
let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?;
if let Some(bytes) = state.payload_cache.get(entry) {
return Ok(ResourceBytes::Shared(bytes));
}
state.entry_archive(entry)?;
state.payload_cache.insert(entry, Arc::clone(&shared)); state.payload_cache.insert(entry, Arc::clone(&shared));
Ok(ResourceBytes::Shared(shared)) Ok(ResourceBytes::Shared(shared))
} }
@@ -368,7 +379,7 @@ impl ResourceRepository for CachedResourceRepository {
fn entry_info(&self, entry: EntryHandle) -> Result<ResourceEntryInfo, ResourceError> { fn entry_info(&self, entry: EntryHandle) -> Result<ResourceEntryInfo, ResourceError> {
let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?;
let slot = state.entry_archive(entry)?; let slot = state.entry_archive(entry)?;
match &slot.document { match slot.document.as_ref() {
ArchiveDocument::Nres(document) => { ArchiveDocument::Nres(document) => {
let local = let local =
usize::try_from(entry.local).map_err(|_| ResourceError::InvalidHandle)?; usize::try_from(entry.local).map_err(|_| ResourceError::InvalidHandle)?;
@@ -512,11 +523,19 @@ impl RepositoryState {
} }
Ok(slot) Ok(slot)
} }
fn payload_decode_task(&self, entry: EntryHandle) -> Result<PayloadDecodeTask, ResourceError> {
let slot = self.entry_archive(entry)?;
Ok(PayloadDecodeTask {
document: Arc::clone(&slot.document),
key: slot.entry_key(entry.local)?,
})
}
} }
impl ArchiveSlot { impl ArchiveSlot {
fn entry_key(&self, local: u32) -> Result<ResourceKey, ResourceError> { fn entry_key(&self, local: u32) -> Result<ResourceKey, ResourceError> {
match &self.document { match self.document.as_ref() {
ArchiveDocument::Nres(document) => { ArchiveDocument::Nres(document) => {
let local = usize::try_from(local).map_err(|_| ResourceError::InvalidHandle)?; let local = usize::try_from(local).map_err(|_| ResourceError::InvalidHandle)?;
let entry = document let entry = document
@@ -541,9 +560,11 @@ impl ArchiveSlot {
} }
} }
} }
}
impl ArchiveDocument {
fn read_payload(&self, local: u32) -> Result<Vec<u8>, String> { fn read_payload(&self, local: u32) -> Result<Vec<u8>, String> {
match &self.document { match self {
ArchiveDocument::Nres(document) => document ArchiveDocument::Nres(document) => document
.payload(fparkan_nres::EntryId(local)) .payload(fparkan_nres::EntryId(local))
.map(<[u8]>::to_vec) .map(<[u8]>::to_vec)
@@ -568,7 +589,7 @@ fn decode_archive(
fingerprint, fingerprint,
generation: 0, generation: 0,
kind: ArchiveKind::Nres, kind: ArchiveKind::Nres,
document: ArchiveDocument::Nres(document), document: Arc::new(ArchiveDocument::Nres(document)),
}); });
} }
if bytes.get(0..4) == Some(b"NL\0\x01") { if bytes.get(0..4) == Some(b"NL\0\x01") {
@@ -579,7 +600,7 @@ fn decode_archive(
fingerprint, fingerprint,
generation: 0, generation: 0,
kind: ArchiveKind::Rsli, kind: ArchiveKind::Rsli,
document: ArchiveDocument::Rsli(document), document: Arc::new(ArchiveDocument::Rsli(document)),
}); });
} }
Err(ResourceError::Format( Err(ResourceError::Format(