relay_knowledge/watcher/hash_cache/
mod.rs1use std::collections::{HashMap, VecDeque};
2use std::path::PathBuf;
3
4use crate::identity::stable_hash64;
5
6#[derive(Debug, Clone)]
7pub struct ContentHashCache {
8 capacity: usize,
9 entries: HashMap<PathBuf, u64>,
10 insertion_order: VecDeque<PathBuf>,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct ContentHashObservation {
15 pub changed: bool,
16 pub hash: u64,
17}
18
19impl ContentHashCache {
20 pub fn new(capacity: usize) -> Self {
21 Self {
22 capacity,
23 entries: HashMap::new(),
24 insertion_order: VecDeque::new(),
25 }
26 }
27
28 pub fn check_and_update(&mut self, path: PathBuf, content: &[u8]) -> ContentHashObservation {
29 self.check_hash_and_update(path, content_hash64(content))
30 }
31
32 pub fn check_hash_and_update(
33 &mut self,
34 path: PathBuf,
35 new_hash: u64,
36 ) -> ContentHashObservation {
37 let observation = self.observe_hash(&path, new_hash);
38 if observation.changed {
39 self.record_hash(path, new_hash);
40 }
41 observation
42 }
43
44 pub fn observe_hash(&self, path: &PathBuf, new_hash: u64) -> ContentHashObservation {
45 if self.capacity == 0 {
46 return ContentHashObservation {
47 changed: true,
48 hash: new_hash,
49 };
50 }
51 let changed = match self.entries.get(path) {
52 Some(&existing) => existing != new_hash,
53 None => true,
54 };
55 ContentHashObservation {
56 changed,
57 hash: new_hash,
58 }
59 }
60
61 pub fn record_hash(&mut self, path: PathBuf, new_hash: u64) {
62 if self.capacity == 0 {
63 return;
64 }
65 if self.entries.len() >= self.capacity && !self.entries.contains_key(&path) {
66 self.evict_oldest();
67 }
68 if !self.entries.contains_key(&path) {
69 self.insertion_order.push_back(path.clone());
70 }
71 self.entries.insert(path, new_hash);
72 }
73
74 pub fn remove(&mut self, path: &PathBuf) {
75 self.entries.remove(path);
76 self.insertion_order.retain(|entry| entry != path);
77 }
78
79 pub fn len(&self) -> usize {
80 self.entries.len()
81 }
82
83 pub fn is_empty(&self) -> bool {
84 self.entries.is_empty()
85 }
86
87 pub fn capacity(&self) -> usize {
88 self.capacity
89 }
90
91 pub fn clear(&mut self) {
92 self.entries.clear();
93 self.insertion_order.clear();
94 }
95
96 pub(crate) fn snapshots(&self) -> Vec<(PathBuf, u64)> {
97 self.insertion_order
98 .iter()
99 .filter_map(|path| self.entries.get(path).map(|hash| (path.clone(), *hash)))
100 .collect()
101 }
102
103 fn evict_oldest(&mut self) {
104 while let Some(oldest_key) = self.insertion_order.pop_front() {
105 if self.entries.remove(&oldest_key).is_some() {
106 break;
107 }
108 }
109 }
110}
111
112pub(super) fn content_hash64(bytes: &[u8]) -> u64 {
113 stable_hash64(bytes)
114}
115
116#[cfg(test)]
117#[path = "mod_tests.rs"]
118mod tests;