use std::collections::HashMap;
use std::hash::Hash;
use std::path::PathBuf;
use std::sync::{Arc, OnceLock, RwLock};
use super::mock_host::MockHostScan;
use super::preflight::ModuleScan;
#[derive(Default)]
pub(crate) struct ScanMemo {
preflight: MemoMap<(PathBuf, [u8; 32]), Option<Arc<ModuleScan>>>,
mock_host: MemoMap<PathBuf, Option<Arc<MockHostScan>>>,
}
impl ScanMemo {
pub(super) fn preflight(
&self,
canonical: &std::path::Path,
context: [u8; 32],
build: impl FnOnce() -> Option<ModuleScan>,
) -> Option<Arc<ModuleScan>> {
self.preflight
.get_or_init((canonical.to_path_buf(), context), || build().map(Arc::new))
}
pub(super) fn mock_host(
&self,
canonical: &std::path::Path,
build: impl FnOnce() -> Option<MockHostScan>,
) -> Option<Arc<MockHostScan>> {
self.mock_host
.get_or_init(canonical.to_path_buf(), || build().map(Arc::new))
}
}
struct MemoMap<K, V> {
slots: RwLock<HashMap<K, Arc<OnceLock<V>>>>,
}
impl<K, V> Default for MemoMap<K, V> {
fn default() -> Self {
Self {
slots: RwLock::new(HashMap::new()),
}
}
}
impl<K: Eq + Hash, V: Clone> MemoMap<K, V> {
fn get_or_init(&self, key: K, init: impl FnOnce() -> V) -> V {
let existing = self
.slots
.read()
.expect("check scan memo lock poisoned")
.get(&key)
.cloned();
let slot = existing.unwrap_or_else(|| {
self.slots
.write()
.expect("check scan memo lock poisoned")
.entry(key)
.or_default()
.clone()
});
slot.get_or_init(init).clone()
}
}