bytesandbrains_core/
address.rs1use std::fmt;
2use std::hash::{Hash, Hasher};
3use std::num::NonZeroUsize;
4use std::str::FromStr;
5
6use lru::LruCache;
7
8pub trait Address: Hash + Eq + Clone + ToString + FromStr + fmt::Debug {}
13
14impl<T: Hash + Eq + Clone + ToString + FromStr + fmt::Debug> Address for T {}
15
16pub struct AddressBook<A: Address> {
22 cache: LruCache<A, ()>,
23 max_size: usize,
24}
25
26impl<A: Address> Clone for AddressBook<A> {
27 fn clone(&self) -> Self {
28 let mut cache = LruCache::new(NonZeroUsize::new(self.max_size).unwrap());
29 for (addr, _) in self.cache.iter().rev() {
30 cache.put(addr.clone(), ());
31 }
32 Self {
33 cache,
34 max_size: self.max_size,
35 }
36 }
37}
38
39impl<A: Address> PartialEq for AddressBook<A> {
40 fn eq(&self, other: &Self) -> bool {
41 if self.cache.len() != other.cache.len() {
42 return false;
43 }
44 self.cache
45 .iter()
46 .zip(other.cache.iter())
47 .all(|((a, _), (b, _))| a == b)
48 }
49}
50
51impl<A: Address> Eq for AddressBook<A> {}
52
53impl<A: Address> Hash for AddressBook<A> {
54 fn hash<H: Hasher>(&self, state: &mut H) {
55 for (addr, _) in self.cache.iter() {
56 addr.hash(state);
57 }
58 }
59}
60
61impl<A: Address> AddressBook<A> {
62 pub fn new(first_addr: A, max_size: usize) -> AddressBook<A> {
63 assert!(max_size > 0, "AddressBook max_size must be > 0");
64 let mut cache = LruCache::new(NonZeroUsize::new(max_size).unwrap());
65 cache.put(first_addr, ());
66 AddressBook { cache, max_size }
67 }
68
69 pub fn seen(&mut self, addr: A) -> bool {
73 self.cache.put(addr, ()).is_none()
74 }
75
76 pub fn get(&self, index: usize) -> Option<&A> {
77 self.cache.iter().nth(index).map(|(addr, _)| addr)
78 }
79
80 pub fn first(&self) -> &A {
82 self.cache
83 .iter()
84 .next()
85 .map(|(addr, _)| addr)
86 .expect("AddressBook is never empty")
87 }
88
89 pub fn iter(&self) -> impl Iterator<Item = &A> {
91 self.cache.iter().map(|(addr, _)| addr)
92 }
93
94 pub fn len(&self) -> usize {
95 self.cache.len()
96 }
97
98 pub fn max_size(&self) -> usize {
99 self.max_size
100 }
101
102 pub fn into_vec(self) -> Vec<A> {
104 self.cache.into_iter().map(|(addr, _)| addr).collect()
105 }
106
107 pub fn contains(&self, addr: &A) -> bool {
108 self.cache.contains(addr)
109 }
110
111 pub fn remove(&mut self, addr: &A) -> bool {
112 if self.cache.len() <= 1 {
113 return false;
114 }
115 self.cache.pop(addr).is_some()
116 }
117
118 pub fn is_empty(&self) -> bool {
119 self.cache.is_empty()
120 }
121
122 pub fn concat(&mut self, other: &AddressBook<A>) -> usize {
125 let mut added = 0;
126 for (addr, _) in other.cache.iter().rev() {
128 if !self.cache.contains(addr) && self.cache.len() < self.max_size {
129 self.cache.push(addr.clone(), ());
131 added += 1;
132 }
133 }
134 added
135 }
136}
137
138impl<A: Address + fmt::Debug> fmt::Debug for AddressBook<A> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 let addrs: Vec<&A> = self.cache.iter().map(|(a, _)| a).collect();
141 f.debug_struct("AddressBook")
142 .field("addrs", &addrs)
143 .field("max_size", &self.max_size)
144 .finish()
145 }
146}
147
148impl<A: Address> fmt::Display for AddressBook<A> {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 let addrs: Vec<String> = self.iter().map(|a| a.to_string()).collect();
151 write!(f, "[{}] (max {})", addrs.join(", "), self.max_size)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_new() {
161 let page = AddressBook::new("addr1".to_string(), 5);
162 assert_eq!(page.len(), 1);
163 assert_eq!(page.first(), "addr1");
164 assert_eq!(page.max_size(), 5);
165 }
166
167 #[test]
168 fn test_seen_new_address() {
169 let mut page = AddressBook::new("addr1".to_string(), 5);
170 assert!(page.seen("addr2".to_string()));
171 assert_eq!(page.len(), 2);
172 assert_eq!(page.first(), "addr2");
173 }
174
175 #[test]
176 fn test_seen_existing_promotes() {
177 let mut page = AddressBook::new("addr1".to_string(), 5);
178 page.seen("addr2".to_string());
179 assert!(!page.seen("addr1".to_string()));
181 assert_eq!(page.first(), "addr1");
183 }
184
185 #[test]
186 fn test_eviction_at_capacity() {
187 let mut page = AddressBook::new("addr1".to_string(), 2);
188 page.seen("addr2".to_string());
189 assert_eq!(page.len(), 2);
190 page.seen("addr3".to_string());
191 assert_eq!(page.len(), 2);
192 assert!(!page.contains(&"addr1".to_string()));
194 assert!(page.contains(&"addr3".to_string()));
195 }
196
197 #[test]
198 fn test_contains() {
199 let mut page = AddressBook::new("addr1".to_string(), 5);
200 assert!(page.contains(&"addr1".to_string()));
201 assert!(!page.contains(&"addr2".to_string()));
202 page.seen("addr2".to_string());
203 assert!(page.contains(&"addr2".to_string()));
204 }
205
206 #[test]
207 fn test_remove() {
208 let mut page = AddressBook::new("addr1".to_string(), 5);
209 page.seen("addr2".to_string());
210 assert!(page.remove(&"addr1".to_string()));
211 assert!(!page.contains(&"addr1".to_string()));
212 assert!(!page.remove(&"addr1".to_string()));
213 }
214
215 #[test]
216 fn test_remove_last_address_refused() {
217 let mut page = AddressBook::new("addr1".to_string(), 5);
218 assert!(!page.remove(&"addr1".to_string()));
219 assert_eq!(page.len(), 1);
220 assert!(page.contains(&"addr1".to_string()));
221 }
222
223 #[test]
224 fn test_concat() {
225 let mut page1 = AddressBook::new("a".to_string(), 5);
226 page1.seen("b".to_string());
227
228 let mut page2 = AddressBook::new("c".to_string(), 5);
229 page2.seen("d".to_string());
230
231 let added = page1.concat(&page2);
232 assert_eq!(added, 2);
233 assert_eq!(page1.len(), 4);
234 }
235
236 #[test]
237 fn test_concat_respects_capacity() {
238 let mut page1 = AddressBook::new("a".to_string(), 3);
239 page1.seen("b".to_string());
240
241 let mut page2 = AddressBook::new("c".to_string(), 5);
242 page2.seen("d".to_string());
243 page2.seen("e".to_string());
244
245 let added = page1.concat(&page2);
246 assert_eq!(added, 1); assert_eq!(page1.len(), 3);
248 }
249
250 #[test]
251 fn test_into_vec() {
252 let mut page = AddressBook::new("a".to_string(), 5);
253 page.seen("b".to_string());
254 let v = page.into_vec();
255 assert_eq!(v.len(), 2);
256 }
257
258 #[test]
259 fn test_equality() {
260 let mut page1 = AddressBook::new("a".to_string(), 5);
261 page1.seen("b".to_string());
262 let mut page2 = AddressBook::new("a".to_string(), 5);
263 page2.seen("b".to_string());
264 assert_eq!(page1, page2);
265 }
266
267 #[test]
268 fn test_socket_addr_as_address() {
269 use std::net::SocketAddr;
270 let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
271 let page = AddressBook::new(addr, 5);
272 assert_eq!(page.len(), 1);
273 }
274}