ant_core/data/client/
cache.rs1use ant_protocol::XorName;
7use bytes::Bytes;
8use lru::LruCache;
9use std::num::NonZeroUsize;
10use std::sync::{Mutex, PoisonError};
11
12const DEFAULT_CACHE_CAPACITY: usize = 1024;
14
15pub struct ChunkCache {
17 inner: Mutex<LruCache<XorName, Bytes>>,
18}
19
20impl ChunkCache {
21 #[must_use]
25 pub fn new(capacity: usize) -> Self {
26 let effective = if capacity == 0 {
28 DEFAULT_CACHE_CAPACITY
29 } else {
30 capacity
31 };
32 let cap = NonZeroUsize::new(effective).unwrap_or(NonZeroUsize::MIN);
34 Self {
35 inner: Mutex::new(LruCache::new(cap)),
36 }
37 }
38
39 #[must_use]
41 pub fn with_default_capacity() -> Self {
42 Self::new(DEFAULT_CACHE_CAPACITY)
43 }
44
45 #[must_use]
47 pub fn get(&self, address: &XorName) -> Option<Bytes> {
48 let mut cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
49 cache.get(address).cloned()
50 }
51
52 pub fn put(&self, address: XorName, content: Bytes) {
54 let mut cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
55 cache.put(address, content);
56 }
57
58 #[must_use]
60 pub fn contains(&self, address: &XorName) -> bool {
61 let cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
62 cache.contains(address)
63 }
64
65 #[must_use]
67 pub fn len(&self) -> usize {
68 let cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
69 cache.len()
70 }
71
72 #[must_use]
74 pub fn is_empty(&self) -> bool {
75 self.len() == 0
76 }
77
78 pub fn remove(&self, address: &XorName) {
80 let mut cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
81 cache.pop(address);
82 }
83
84 pub fn clear(&self) {
86 let mut cache = self.inner.lock().unwrap_or_else(PoisonError::into_inner);
87 cache.clear();
88 }
89}
90
91impl Default for ChunkCache {
92 fn default() -> Self {
93 Self::with_default_capacity()
94 }
95}
96
97#[cfg(test)]
98#[allow(clippy::unwrap_used, clippy::expect_used)]
99mod tests {
100 use super::*;
101
102 fn make_address(byte: u8) -> XorName {
103 let mut addr = [0u8; 32];
104 addr[0] = byte;
105 addr
106 }
107
108 #[test]
109 fn test_new_cache_default_capacity() {
110 let cache = ChunkCache::default();
111 assert!(cache.is_empty());
112 assert_eq!(cache.len(), 0);
113 }
114
115 #[test]
116 fn test_zero_capacity_falls_back_to_default() {
117 let cache = ChunkCache::new(0);
118 let addr = make_address(1);
119 cache.put(addr, Bytes::from_static(b"hello"));
120 assert_eq!(cache.len(), 1);
121 }
122
123 #[test]
124 fn test_put_and_get() {
125 let cache = ChunkCache::new(10);
126 let addr = make_address(1);
127 let data = Bytes::from_static(b"hello world");
128
129 cache.put(addr, data.clone());
130 let got = cache.get(&addr);
131 assert_eq!(got, Some(data));
132 }
133
134 #[test]
135 fn test_get_miss() {
136 let cache = ChunkCache::new(10);
137 let addr = make_address(1);
138 assert_eq!(cache.get(&addr), None);
139 }
140
141 #[test]
142 fn test_contains() {
143 let cache = ChunkCache::new(10);
144 let addr = make_address(1);
145 assert!(!cache.contains(&addr));
146
147 cache.put(addr, Bytes::from_static(b"data"));
148 assert!(cache.contains(&addr));
149 }
150
151 #[test]
152 fn test_lru_eviction() {
153 let cache = ChunkCache::new(2);
154
155 let addr1 = make_address(1);
156 let addr2 = make_address(2);
157 let addr3 = make_address(3);
158
159 cache.put(addr1, Bytes::from_static(b"one"));
160 cache.put(addr2, Bytes::from_static(b"two"));
161 assert_eq!(cache.len(), 2);
162
163 cache.put(addr3, Bytes::from_static(b"three"));
165 assert_eq!(cache.len(), 2);
166 assert!(!cache.contains(&addr1));
167 assert!(cache.contains(&addr2));
168 assert!(cache.contains(&addr3));
169 }
170
171 #[test]
172 fn test_lru_access_refreshes() {
173 let cache = ChunkCache::new(2);
174
175 let addr1 = make_address(1);
176 let addr2 = make_address(2);
177 let addr3 = make_address(3);
178
179 cache.put(addr1, Bytes::from_static(b"one"));
180 cache.put(addr2, Bytes::from_static(b"two"));
181
182 let _ = cache.get(&addr1);
184
185 cache.put(addr3, Bytes::from_static(b"three"));
187 assert!(cache.contains(&addr1));
188 assert!(!cache.contains(&addr2));
189 assert!(cache.contains(&addr3));
190 }
191
192 #[test]
193 fn test_clear() {
194 let cache = ChunkCache::new(10);
195 cache.put(make_address(1), Bytes::from_static(b"one"));
196 cache.put(make_address(2), Bytes::from_static(b"two"));
197 assert_eq!(cache.len(), 2);
198
199 cache.clear();
200 assert!(cache.is_empty());
201 assert_eq!(cache.len(), 0);
202 }
203
204 #[test]
205 fn test_overwrite_same_key() {
206 let cache = ChunkCache::new(10);
207 let addr = make_address(1);
208
209 cache.put(addr, Bytes::from_static(b"first"));
210 cache.put(addr, Bytes::from_static(b"second"));
211
212 assert_eq!(cache.len(), 1);
213 assert_eq!(cache.get(&addr), Some(Bytes::from_static(b"second")));
214 }
215}