1use std::{
2 collections::VecDeque,
3 sync::{
4 Arc,
5 Mutex,
6 },
7};
8
9use blitz_traits::net::Bytes;
10use freya_core::prelude::GlobalContexts;
11use url::Url;
12
13#[derive(Default)]
14struct CachedResources {
15 entries: VecDeque<(Url, Bytes)>,
16 bytes: usize,
17}
18
19impl CachedResources {
20 fn remove(&mut self, url: &Url) -> Option<(Url, Bytes)> {
21 let index = self
22 .entries
23 .iter()
24 .position(|(cached_url, _)| cached_url == url)?;
25 let entry = self.entries.remove(index)?;
26 self.bytes -= entry.1.len();
27 Some(entry)
28 }
29}
30
31#[derive(Clone)]
33pub struct ResourceCache {
34 resources: Arc<Mutex<CachedResources>>,
35 max_bytes: usize,
36}
37
38impl Default for ResourceCache {
39 fn default() -> Self {
40 Self::new(32 * 1024 * 1024)
41 }
42}
43
44impl ResourceCache {
45 pub fn new(max_bytes: usize) -> Self {
55 Self {
56 resources: Arc::new(Mutex::new(CachedResources::default())),
57 max_bytes,
58 }
59 }
60
61 pub fn get() -> Self {
63 GlobalContexts::get().get_context_or_insert(Self::default)
64 }
65
66 pub fn read(&self, url: &Url) -> Option<Bytes> {
68 let mut resources = self
69 .resources
70 .lock()
71 .unwrap_or_else(|error| error.into_inner());
72 let index = resources
73 .entries
74 .iter()
75 .position(|(cached_url, _)| cached_url == url)?;
76 let entry = resources.entries.remove(index)?;
77 let bytes = entry.1.clone();
78 resources.entries.push_back(entry);
79 Some(bytes)
80 }
81
82 pub fn set(&self, url: Url, bytes: Bytes) {
84 let mut resources = self
85 .resources
86 .lock()
87 .unwrap_or_else(|error| error.into_inner());
88 resources.remove(&url);
89 if bytes.is_empty() || bytes.len() > self.max_bytes {
90 return;
91 }
92
93 while resources.bytes > self.max_bytes - bytes.len()
94 && let Some((_, evicted)) = resources.entries.pop_front()
95 {
96 resources.bytes -= evicted.len();
97 }
98 resources.bytes += bytes.len();
99 resources.entries.push_back((url, bytes));
100 }
101
102 pub fn invalidate(&self, url: &Url) {
103 self.resources
104 .lock()
105 .unwrap_or_else(|error| error.into_inner())
106 .remove(url);
107 }
108
109 pub fn clear(&self) {
110 *self
111 .resources
112 .lock()
113 .unwrap_or_else(|error| error.into_inner()) = CachedResources::default();
114 }
115}