use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::node::ImageData;
use crate::util::ImageType;
pub use bliss_traits::image_cache::{
create_default_image_cache, create_image_cache_with_limit, create_image_cache_with_limits,
create_image_cache_with_ttl, ImageCache as TraitImageCache, LruImageCache, SharedImageCache,
};
pub trait DomImageCache: Send + Sync + 'static {
fn get(&self, url: &str) -> Option<Arc<ImageData>>;
fn insert(&self, url: String, image: ImageData);
fn evict(&self, url: &str);
fn memory_usage(&self) -> usize;
fn clear(&self);
}
pub struct DomImageCacheAdapter {
cache: Mutex<HashMap<String, Arc<ImageData>>>,
max_bytes: usize,
current_bytes: Mutex<usize>,
}
impl DomImageCacheAdapter {
pub fn new(_inner: SharedImageCache) -> Self {
Self {
cache: Mutex::new(HashMap::new()),
max_bytes: 100 * 1024 * 1024, current_bytes: Mutex::new(0),
}
}
pub fn with_limit(_inner: SharedImageCache, max_bytes: usize) -> Self {
Self {
cache: Mutex::new(HashMap::new()),
max_bytes,
current_bytes: Mutex::new(0),
}
}
}
fn image_size(image: &ImageData) -> usize {
match image {
ImageData::Raster(raster) => raster.data.len(),
#[cfg(feature = "svg")]
ImageData::Svg(_) => 1024, ImageData::None => 0,
}
}
impl DomImageCache for DomImageCacheAdapter {
fn get(&self, url: &str) -> Option<Arc<ImageData>> {
let cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
cache.get(url).cloned()
}
fn insert(&self, url: String, image: ImageData) {
let size = image_size(&image);
let mut cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
let mut current_bytes = self.current_bytes.lock().unwrap_or_else(|e| e.into_inner());
if let Some(old_entry) = cache.get(&url) {
let old_size = image_size(old_entry.as_ref());
*current_bytes = current_bytes.saturating_sub(old_size);
}
cache.insert(url, Arc::new(image));
*current_bytes += size;
}
fn evict(&self, url: &str) {
let mut cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
let mut current_bytes = self.current_bytes.lock().unwrap_or_else(|e| e.into_inner());
if let Some(old_entry) = cache.remove(url) {
let old_size = image_size(old_entry.as_ref());
*current_bytes = current_bytes.saturating_sub(old_size);
}
}
fn memory_usage(&self) -> usize {
*self.current_bytes.lock().unwrap_or_else(|e| e.into_inner())
}
fn clear(&self) {
let mut cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
let mut current_bytes = self.current_bytes.lock().unwrap_or_else(|e| e.into_inner());
cache.clear();
*current_bytes = 0;
}
}
pub type DomSharedImageCache = Arc<dyn DomImageCache>;
pub fn create_default_dom_image_cache() -> DomSharedImageCache {
Arc::new(DomImageCacheAdapter::new(create_default_image_cache()))
}
pub fn create_dom_image_cache_with_limit(max_bytes: usize) -> DomSharedImageCache {
Arc::new(DomImageCacheAdapter::with_limit(
create_image_cache_with_limit(max_bytes),
max_bytes,
))
}
pub fn create_dom_image_cache_with_limits(
max_bytes: usize,
max_entries: usize,
) -> DomSharedImageCache {
Arc::new(DomImageCacheAdapter::new(create_image_cache_with_limits(
max_bytes,
max_entries,
)))
}
pub fn create_dom_image_cache_with_ttl(max_bytes: usize, ttl: Duration) -> DomSharedImageCache {
Arc::new(DomImageCacheAdapter::new(create_image_cache_with_ttl(
max_bytes, ttl,
)))
}
pub struct PendingImageRegistry {
pending: Mutex<HashMap<String, Vec<(usize, ImageType)>>>,
}
impl PendingImageRegistry {
pub fn new() -> Self {
Self {
pending: Mutex::new(HashMap::new()),
}
}
pub fn register(&self, url: String, node_id: usize, image_type: ImageType) {
let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
pending.entry(url).or_default().push((node_id, image_type));
}
pub fn take_waiting(&self, url: &str) -> Vec<(usize, ImageType)> {
let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
pending.remove(url).unwrap_or_default()
}
pub fn has_waiting(&self, url: &str) -> bool {
let pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
pending.contains_key(url)
}
pub fn clear(&self) {
let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
pending.clear();
}
pub fn pending_count(&self) -> usize {
let pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
pending.len()
}
}
impl Default for PendingImageRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct ImageManager {
pub cache: DomSharedImageCache,
pub pending: Arc<PendingImageRegistry>,
}
impl ImageManager {
pub fn new() -> Self {
Self {
cache: create_default_dom_image_cache(),
pending: Arc::new(PendingImageRegistry::new()),
}
}
pub fn with_cache(cache: DomSharedImageCache) -> Self {
Self {
cache,
pending: Arc::new(PendingImageRegistry::new()),
}
}
pub fn get_cached(&self, url: &str) -> Option<Arc<ImageData>> {
self.cache.get(url)
}
pub fn cache_image(&self, url: String, image: ImageData) {
self.cache.insert(url, image);
}
pub fn evict_image(&self, url: &str) {
self.cache.evict(url);
}
pub fn memory_usage(&self) -> usize {
self.cache.memory_usage()
}
pub fn clear_cache(&self) {
self.cache.clear();
}
}
impl Default for ImageManager {
fn default() -> Self {
Self::new()
}
}