1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::fs::File;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use crate::crc_any::CRCu64;
use crate::lru_time_cache::LruCache;
use crate::EntityTag;
#[inline]
fn compute_file_etag<P: AsRef<Path>>(path: P) -> Result<EntityTag, io::Error> {
let mut crc64ecma = CRCu64::crc64();
let mut buffer = [0u8; 4096];
{
let mut file = File::open(path.as_ref())?;
loop {
match file.read(&mut buffer) {
Ok(c) => {
if c == 0 {
break;
}
crc64ecma.digest(&buffer[0..c]);
}
Err(error) => {
return Err(error);
}
}
}
}
let crc64 = crc64ecma.get_crc();
Ok(EntityTag::new(true, format!("{:X}", crc64)))
}
#[derive(Educe)]
#[educe(Debug)]
#[allow(clippy::type_complexity)]
pub struct FileEtagCache {
#[educe(Debug(ignore))]
cache_table: Mutex<LruCache<PathBuf, (Arc<EntityTag>, Option<SystemTime>)>>,
}
impl FileEtagCache {
#[inline]
pub fn new(cache_capacity: usize) -> FileEtagCache {
FileEtagCache {
cache_table: Mutex::new(LruCache::with_capacity(cache_capacity)),
}
}
#[inline]
pub fn clear_cache(&self) {
self.cache_table.lock().unwrap().clear();
}
#[inline]
pub fn contains_key<S: AsRef<Path>>(&self, key: S) -> bool {
self.cache_table.lock().unwrap().get(key.as_ref()).is_some()
}
#[inline]
pub fn get_or_insert<P: AsRef<Path> + Into<PathBuf>>(
&self,
path: P,
) -> io::Result<Arc<EntityTag>> {
let path_ref = path.as_ref();
let mtime = match self
.cache_table
.lock()
.unwrap()
.get(path_ref)
.map(|(etag, mtime)| (etag.clone(), *mtime))
{
Some((etag, mtime)) => {
let metadata = path_ref.metadata()?;
match mtime {
Some(mtime) => {
match metadata.modified() {
Ok(new_mtime) => {
if new_mtime != mtime {
Some(new_mtime)
} else {
return Ok(etag);
}
}
Err(_) => None,
}
}
None => {
match metadata.modified() {
Ok(new_mtime) => Some(new_mtime),
Err(_) => None,
}
}
}
}
None => {
let metadata = path_ref.metadata()?;
match metadata.modified() {
Ok(new_mtime) => Some(new_mtime),
Err(_) => None,
}
}
};
let etag = compute_file_etag(&path)?;
let etag = Arc::new(etag);
self.cache_table.lock().unwrap().insert(path.into(), (etag.clone(), mtime));
Ok(etag)
}
}