use crate::error::{DecodeError, DecodedImage};
use crate::pipeline::decode_with_profile;
use crate::profile::Profile;
use lru::LruCache;
use std::hash::Hasher;
use std::num::NonZeroUsize;
use std::sync::RwLock;
use std::sync::atomic::AtomicBool;
#[derive(Debug)]
pub struct CachedDecoder {
cache: RwLock<LruCache<u64, Vec<u8>>>,
}
impl CachedDecoder {
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn new() -> Self {
Self {
cache: RwLock::new(LruCache::new(NonZeroUsize::new(64).expect("64 is non-zero"))),
}
}
#[allow(clippy::missing_panics_doc)]
pub fn decode_with_cache(
&self,
profile: &Profile,
data: &[u8],
canceled: &AtomicBool,
) -> Result<DecodedImage, DecodeError> {
let key = {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
hasher.write(data);
hasher.finish()
};
{
let mut cache = self.cache.write().unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(entry) = cache.get(&key) {
return Ok(deserialize_entry(entry));
}
}
let image = decode_with_profile(data, profile, canceled)?;
{
let mut cache = self.cache.write().unwrap_or_else(std::sync::PoisonError::into_inner);
cache.put(key, serialize_entry(&image));
}
Ok(image)
}
#[allow(clippy::missing_panics_doc)]
pub fn clear(&self) {
let mut cache = self.cache.write().expect("cache lock poisoned");
cache.clear();
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn len(&self) -> usize {
let cache = self.cache.read().expect("cache lock poisoned");
cache.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for CachedDecoder {
fn default() -> Self {
Self::new()
}
}
fn serialize_entry(img: &DecodedImage) -> Vec<u8> {
let width_bytes = img.width.to_le_bytes();
let height_bytes = img.height.to_le_bytes();
let mut buf = Vec::with_capacity(8 + img.data.len());
buf.extend_from_slice(&width_bytes);
buf.extend_from_slice(&height_bytes);
buf.extend_from_slice(&img.data);
buf
}
fn deserialize_entry(entry: &[u8]) -> DecodedImage {
let width = u32::from_le_bytes([entry[0], entry[1], entry[2], entry[3]]);
let height = u32::from_le_bytes([entry[4], entry[5], entry[6], entry[7]]);
DecodedImage {
data: entry[8..].to_vec(),
width,
height,
}
}