use std::collections::HashMap;
use std::path::Path;
use crate::common::error::Result;
use crate::common::metadata::{ImageMetadata, MetadataOptions};
use crate::common::ome_metadata::OmeMetadata;
use crate::common::reader::FormatReader;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheStrategy {
Lru(usize),
Rectangle(usize),
Crosshair,
None,
}
impl Default for CacheStrategy {
fn default() -> Self {
CacheStrategy::Lru(64)
}
}
pub struct CachedReader {
inner: Box<dyn FormatReader>,
#[allow(dead_code)]
strategy: CacheStrategy,
cache: HashMap<(usize, usize, u32), Vec<u8>>,
access_order: Vec<(usize, usize, u32)>,
max_planes: usize,
}
impl CachedReader {
pub fn new(inner: Box<dyn FormatReader>, strategy: CacheStrategy) -> Self {
let max_planes = match strategy {
CacheStrategy::Lru(n) => n,
CacheStrategy::Rectangle(r) => r
.checked_mul(2)
.and_then(|diameter| diameter.checked_add(1))
.and_then(|diameter| diameter.checked_mul(diameter))
.and_then(|area| area.checked_mul(r.saturating_mul(2).saturating_add(1)))
.unwrap_or(usize::MAX),
CacheStrategy::Crosshair => 256,
CacheStrategy::None => 0,
};
CachedReader {
inner,
strategy,
cache: HashMap::new(),
access_order: Vec::new(),
max_planes,
}
}
pub fn cached_count(&self) -> usize {
self.cache.len()
}
pub fn clear_cache(&mut self) {
self.cache.clear();
self.access_order.clear();
}
fn cache_key(&self, plane_index: u32) -> (usize, usize, u32) {
(self.inner.series(), self.inner.resolution(), plane_index)
}
fn evict_if_needed(&mut self, inserting_new: bool) {
let reserve = if inserting_new { 1 } else { 0 };
while self.cache.len() + reserve > self.max_planes && !self.access_order.is_empty() {
let oldest = self.access_order.remove(0);
self.cache.remove(&oldest);
}
}
fn store(&mut self, key: (usize, usize, u32), data: Vec<u8>) {
if self.max_planes == 0 {
return;
}
let inserting_new = !self.cache.contains_key(&key);
self.evict_if_needed(inserting_new);
self.access_order.retain(|k| k != &key);
self.access_order.push(key);
self.cache.insert(key, data);
}
}
impl FormatReader for CachedReader {
fn is_this_type_by_name(&self, path: &Path) -> bool {
self.inner.is_this_type_by_name(path)
}
fn is_this_type_by_bytes(&self, header: &[u8]) -> bool {
self.inner.is_this_type_by_bytes(header)
}
fn set_id(&mut self, path: &Path) -> Result<()> {
self.clear_cache();
self.inner.set_id(path)
}
fn close(&mut self) -> Result<()> {
self.clear_cache();
self.inner.close()
}
fn series_count(&self) -> usize {
self.inner.series_count()
}
fn set_series(&mut self, series: usize) -> Result<()> {
self.inner.set_series(series)
}
fn series(&self) -> usize {
self.inner.series()
}
fn metadata(&self) -> &ImageMetadata {
self.inner.metadata()
}
fn open_bytes(&mut self, plane_index: u32) -> Result<Vec<u8>> {
let key = self.cache_key(plane_index);
if let Some(data) = self.cache.get(&key) {
self.access_order.retain(|k| k != &key);
self.access_order.push(key);
return Ok(data.clone());
}
let data = self.inner.open_bytes(plane_index)?;
self.store(key, data.clone());
Ok(data)
}
fn open_bytes_region(
&mut self,
plane_index: u32,
x: u32,
y: u32,
w: u32,
h: u32,
) -> Result<Vec<u8>> {
self.inner.open_bytes_region(plane_index, x, y, w, h)
}
fn open_thumb_bytes(&mut self, plane_index: u32) -> Result<Vec<u8>> {
self.inner.open_thumb_bytes(plane_index)
}
fn resolution_count(&self) -> usize {
self.inner.resolution_count()
}
fn set_resolution(&mut self, level: usize) -> Result<()> {
self.inner.set_resolution(level)
}
fn resolution(&self) -> usize {
self.inner.resolution()
}
fn set_metadata_options(&mut self, options: MetadataOptions) {
self.inner.set_metadata_options(options);
}
fn ome_metadata(&self) -> Option<OmeMetadata> {
self.inner.ome_metadata()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::error::BioFormatsError;
use crate::common::metadata::ImageMetadata;
use std::path::Path;
struct EmptyReader;
impl FormatReader for EmptyReader {
fn is_this_type_by_name(&self, _path: &Path) -> bool {
false
}
fn is_this_type_by_bytes(&self, _header: &[u8]) -> bool {
false
}
fn set_id(&mut self, _path: &Path) -> Result<()> {
Ok(())
}
fn close(&mut self) -> Result<()> {
Ok(())
}
fn series_count(&self) -> usize {
1
}
fn set_series(&mut self, _s: usize) -> Result<()> {
Ok(())
}
fn series(&self) -> usize {
0
}
fn metadata(&self) -> &ImageMetadata {
crate::common::reader::uninitialized_metadata()
}
fn open_bytes(&mut self, _plane_index: u32) -> Result<Vec<u8>> {
Err(BioFormatsError::NotInitialized)
}
fn open_bytes_region(
&mut self,
_plane_index: u32,
_x: u32,
_y: u32,
_w: u32,
_h: u32,
) -> Result<Vec<u8>> {
Err(BioFormatsError::NotInitialized)
}
fn open_thumb_bytes(&mut self, _plane_index: u32) -> Result<Vec<u8>> {
Err(BioFormatsError::NotInitialized)
}
}
#[test]
fn rectangle_cache_capacity_saturates_instead_of_overflowing() {
let reader = CachedReader::new(
Box::new(EmptyReader),
CacheStrategy::Rectangle(usize::MAX / 2),
);
assert_eq!(reader.max_planes, usize::MAX);
}
}