pagesift_frontier/frontier/
mod.rs1use std::collections::{HashMap, HashSet, VecDeque};
22
23use url::Url;
24
25mod clean;
26mod learn;
27#[cfg(test)]
28mod tests;
29
30pub use clean::*;
31pub use learn::*;
32
33#[derive(Default)]
38pub struct Tiered {
39 normal: HashMap<String, VecDeque<(Url, usize, u64)>>,
40 ring: VecDeque<String>,
42 normal_len: usize,
43 low: VecDeque<(Url, usize, u64)>,
44 sorted_at: u64,
46}
47
48fn host_of(u: &Url) -> String {
49 u.host_str().unwrap_or_default().to_string()
50}
51
52impl Tiered {
53 pub fn len(&self) -> usize {
54 self.normal_len + self.low.len()
55 }
56
57 pub fn is_empty(&self) -> bool {
58 self.len() == 0
59 }
60
61 pub fn push(&mut self, learner: &Learner, url: Url, depth: usize) {
62 let key = shape_key(&url);
63 if learner.demoted_key(key) {
64 self.low.push_back((url, depth, key));
65 return;
66 }
67 let host = host_of(&url);
68 let q = self.normal.entry(host.clone()).or_default();
69 if q.is_empty() {
70 self.ring.push_back(host);
71 }
72 q.push_back((url, depth, key));
73 self.normal_len += 1;
74 }
75
76 pub fn pop(
81 &mut self,
82 learner: &Learner,
83 allow_low: bool,
84 busy: impl Fn(&str) -> bool,
85 ) -> Option<(Url, usize, bool)> {
86 for _ in 0..self.ring.len() {
87 let host = self.ring.pop_front()?;
88 if busy(&host) {
89 self.ring.push_back(host);
90 continue;
91 }
92 let q = self.normal.get_mut(&host).expect("ring hosts have queues");
93 let mut found = None;
94 while let Some(item) = q.pop_front() {
95 self.normal_len -= 1;
96 if learner.demoted_key(item.2) {
97 self.low.push_back(item);
98 } else {
99 found = Some((item.0, item.1, false));
100 break;
101 }
102 }
103 if q.is_empty() {
104 self.normal.remove(&host);
105 } else {
106 self.ring.push_back(host);
107 }
108 if found.is_some() {
109 return found;
110 }
111 }
112 if !allow_low || self.low.front().is_some_and(|i| busy(&host_of(&i.0))) {
113 return None;
114 }
115 self.low.pop_front().map(|(u, d, _)| (u, d, true))
116 }
117
118 pub fn evict_low(&mut self, learner: &Learner) -> Option<Url> {
121 if self.sorted_at != learner.generation {
122 self.sorted_at = learner.generation;
123 for q in self.normal.values_mut() {
124 let before = q.len();
125 let mut keep = VecDeque::with_capacity(before);
126 for item in q.drain(..) {
127 if learner.demoted_key(item.2) {
128 self.low.push_back(item);
129 } else {
130 keep.push_back(item);
131 }
132 }
133 self.normal_len -= before - keep.len();
134 *q = keep;
135 }
136 self.normal.retain(|_, q| !q.is_empty());
137 let normal = &self.normal;
138 self.ring.retain(|h| normal.contains_key(h));
139 }
140 self.low.pop_back().map(|(u, _, _)| u)
141 }
142
143 pub fn iter(&self) -> impl Iterator<Item = (&Url, usize)> {
144 self.ring
145 .iter()
146 .flat_map(|h| self.normal[h].iter())
147 .chain(&self.low)
148 .map(|(u, d, _)| (u, *d))
149 }
150}