use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::sync::{LazyLock, RwLock}; use indexmap::{Equivalent, IndexMap}; use ndarray::IxDyn; use crate::error::Error; use crate::readers::ArrayT; static CACHE: LazyLock>>> = LazyLock::new(|| RwLock::new(IndexMap::new())); #[derive(Debug, Clone, Eq, PartialEq)] struct ViewHash { reader: String, path: PathBuf, series: usize, position: usize, c: isize, z: isize, t: isize, } impl Hash for ViewHash { fn hash(&self, state: &mut H) { self.reader.as_str().hash(state); self.path.as_path().hash(state); self.series.hash(state); self.position.hash(state); self.c.hash(state); self.z.hash(state); self.t.hash(state); } } impl ViewHash { fn new(reader: String, path: PathBuf, series: usize, position: usize, c: isize, z: isize, t: isize) -> Self { Self { reader, path, series, position, c, z, t } } } struct ViewEquiv<'a> { reader: &'a str, path: &'a Path, series: usize, position: usize, c: isize, z: isize, t: isize, } impl Hash for ViewEquiv<'_> { fn hash(&self, state: &mut H) { self.reader.hash(state); self.path.hash(state); self.series.hash(state); self.position.hash(state); self.c.hash(state); self.z.hash(state); self.t.hash(state); } } impl<'a> ViewEquiv<'a> { fn new(reader: &'a str, path: &'a Path, series: usize, position: usize, c: isize, z: isize, t: isize) -> ViewEquiv<'a> { Self { reader, path, series, position, c, z, t } } } impl Equivalent> for ViewHash { fn equivalent(&self, key: &ViewEquiv) -> bool { (self.reader == key.reader) && (self.path == key.path) && (self.series == key.series) && (self.position == key.position) && (self.c == key.c) && (self.z == key.z) && (self.t == key.t) } } impl Equivalent for ViewEquiv<'_> { fn equivalent(&self, key: &ViewHash) -> bool { key.equivalent(self) } } pub fn cache_get_or_insert(f: &dyn Fn() -> ArrayT, reader: &str, path: &Path, series: usize, position: usize, c: isize, z: isize, t: isize) -> Result, Error> { if let Some(frame) = CACHE.read().unwrap().get(&ViewEquiv::new(reader, path, series, position, c, z, t)) { Ok(frame.clone()) } else { let a = f(); let mut cache = CACHE.write().unwrap(); cache.insert(ViewHash::new(reader.to_string(), path.to_path_buf(), series, position, c, z, t), a.clone()); // TODO: find an IndexMapDeque to pop efficiently at the other end while cache.len() > 1024 { cache.pop(); } Ok(a) } }