Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.46/src/limited_cache.rs
Line
Count
Source
1
use alloc::collections::VecDeque;
2
use core::borrow::Borrow;
3
use core::hash::Hash;
4
5
use crate::hash_map::{Entry, HashMap};
6
7
/// A HashMap-alike, which never gets larger than a specified
8
/// capacity, and evicts the oldest insertion to maintain this.
9
///
10
/// The requested capacity may be rounded up by the underlying
11
/// collections.  This implementation uses all the allocated
12
/// storage.
13
///
14
/// This is inefficient: it stores keys twice.
15
pub(crate) struct LimitedCache<K: Clone + Hash + Eq, V> {
16
    map: HashMap<K, V>,
17
18
    // first item is the oldest key
19
    oldest: VecDeque<K>,
20
21
    /// Maximum number of entries to retain. Compared after insert so
22
    /// a capacity of 1 still keeps the item that was just added.
23
    max: usize,
24
}
25
26
impl<K, V> LimitedCache<K, V>
27
where
28
    K: Eq + Hash + Clone + core::fmt::Debug,
29
    V: Default,
30
{
31
0
    pub(crate) fn get_or_insert_default_and_edit(&mut self, k: K, edit: impl FnOnce(&mut V)) {
32
0
        let inserted_new_item = match self.map.entry(k) {
33
0
            Entry::Occupied(value) => {
34
0
                edit(value.into_mut());
35
0
                false
36
            }
37
0
            entry @ Entry::Vacant(_) => {
38
0
                self.oldest
39
0
                    .push_back(entry.key().clone());
40
0
                edit(entry.or_insert_with(V::default));
41
0
                true
42
            }
43
        };
44
45
0
        if inserted_new_item && self.oldest.len() > self.max {
46
0
            if let Some(oldest_key) = self.oldest.pop_front() {
47
0
                self.map.remove(&oldest_key);
48
0
            }
49
0
        }
50
0
    }
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<rustls_pki_types::server_name::ServerName, rustls::client::handy::cache::ServerData>>::get_or_insert_default_and_edit::<<rustls::client::handy::cache::ClientSessionMemoryCache as rustls::client::client_conn::ClientSessionStore>::set_kx_hint::{closure#0}>
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<rustls_pki_types::server_name::ServerName, rustls::client::handy::cache::ServerData>>::get_or_insert_default_and_edit::<<rustls::client::handy::cache::ClientSessionMemoryCache as rustls::client::client_conn::ClientSessionStore>::set_tls12_session::{closure#0}>
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<rustls_pki_types::server_name::ServerName, rustls::client::handy::cache::ServerData>>::get_or_insert_default_and_edit::<<rustls::client::handy::cache::ClientSessionMemoryCache as rustls::client::client_conn::ClientSessionStore>::insert_tls13_ticket::{closure#0}>
51
52
0
    pub(crate) fn get_mut<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<&mut V>
53
0
    where
54
0
        K: Borrow<Q>,
55
    {
56
0
        self.map.get_mut(k)
57
0
    }
58
}
59
60
impl<K, V> LimitedCache<K, V>
61
where
62
    K: Eq + Hash + Clone + core::fmt::Debug,
63
    V: Default,
64
{
65
    /// Create a new LimitedCache with the given rough capacity.
66
0
    pub(crate) fn new(capacity_order_of_magnitude: usize) -> Self {
67
0
        Self {
68
0
            map: HashMap::with_capacity(capacity_order_of_magnitude),
69
0
            oldest: VecDeque::with_capacity(capacity_order_of_magnitude),
70
0
            max: capacity_order_of_magnitude,
71
0
        }
72
0
    }
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<alloc::vec::Vec<u8>, alloc::vec::Vec<u8>>>::new
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<rustls_pki_types::server_name::ServerName, rustls::client::handy::cache::ServerData>>::new
73
74
0
    pub(crate) fn insert(&mut self, k: K, v: V) {
75
0
        let inserted_new_item = match self.map.entry(k) {
76
0
            Entry::Occupied(mut old) => {
77
                // Note: does not freshen entry in `oldest`
78
0
                old.insert(v);
79
0
                false
80
            }
81
82
0
            entry @ Entry::Vacant(_) => {
83
0
                self.oldest
84
0
                    .push_back(entry.key().clone());
85
0
                entry.or_insert(v);
86
0
                true
87
            }
88
        };
89
90
0
        if inserted_new_item && self.oldest.len() > self.max {
91
0
            if let Some(oldest_key) = self.oldest.pop_front() {
92
0
                self.map.remove(&oldest_key);
93
0
            }
94
0
        }
95
0
    }
96
97
0
    pub(crate) fn get<Q: Hash + Eq + ?Sized>(&self, k: &Q) -> Option<&V>
98
0
    where
99
0
        K: Borrow<Q>,
100
    {
101
0
        self.map.get(k)
102
0
    }
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<alloc::vec::Vec<u8>, alloc::vec::Vec<u8>>>::get::<[u8]>
Unexecuted instantiation: <rustls::limited_cache::LimitedCache<rustls_pki_types::server_name::ServerName, rustls::client::handy::cache::ServerData>>::get::<rustls_pki_types::server_name::ServerName>
103
104
0
    pub(crate) fn remove<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<V>
105
0
    where
106
0
        K: Borrow<Q>,
107
    {
108
0
        let value = self.map.remove(k)?;
109
110
        // O(N) search, followed by O(N) removal
111
0
        if let Some(index) = self
112
0
            .oldest
113
0
            .iter()
114
0
            .position(|item| item.borrow() == k)
115
0
        {
116
0
            self.oldest.remove(index);
117
0
        }
118
119
0
        Some(value)
120
0
    }
121
}
122
123
#[cfg(test)]
124
mod tests {
125
    use std::prelude::v1::*;
126
127
    type Test = super::LimitedCache<String, usize>;
128
129
    #[test]
130
    fn test_updates_existing_item() {
131
        let mut t = Test::new(3);
132
        t.insert("abc".into(), 1);
133
        t.insert("abc".into(), 2);
134
        assert_eq!(t.get("abc"), Some(&2));
135
    }
136
137
    #[test]
138
    fn test_evicts_oldest_item() {
139
        let mut t = Test::new(3);
140
        t.insert("abc".into(), 1);
141
        t.insert("def".into(), 2);
142
        t.insert("ghi".into(), 3);
143
144
        assert_eq!(t.get("abc"), Some(&1));
145
        assert_eq!(t.get("def"), Some(&2));
146
        assert_eq!(t.get("ghi"), Some(&3));
147
148
        t.insert("jkl".into(), 4);
149
        assert_eq!(t.get("abc"), None);
150
        assert_eq!(t.get("jkl"), Some(&4));
151
    }
152
153
    #[test]
154
    fn test_capacity_one_retains_the_item() {
155
        let mut t = Test::new(1);
156
        t.insert("abc".into(), 1);
157
        assert_eq!(t.get("abc"), Some(&1));
158
        t.insert("def".into(), 2);
159
        assert_eq!(t.get("abc"), None);
160
        assert_eq!(t.get("def"), Some(&2));
161
    }
162
163
    #[test]
164
    fn test_evicts_second_oldest_item_if_first_removed() {
165
        let mut t = Test::new(3);
166
        t.insert("abc".into(), 1);
167
        t.insert("def".into(), 2);
168
169
        assert_eq!(t.remove("abc"), Some(1));
170
171
        t.insert("ghi".into(), 3);
172
        t.insert("jkl".into(), 4);
173
174
        assert_eq!(t.get("abc"), None);
175
        assert_eq!(t.get("def"), Some(&2));
176
        assert_eq!(t.get("ghi"), Some(&3));
177
        assert_eq!(t.get("jkl"), Some(&4));
178
    }
179
180
    #[test]
181
    fn test_evicts_after_second_oldest_item_removed() {
182
        let mut t = Test::new(3);
183
        t.insert("abc".into(), 1);
184
        t.insert("def".into(), 2);
185
186
        assert_eq!(t.remove("def"), Some(2));
187
        assert_eq!(t.get("abc"), Some(&1));
188
189
        t.insert("ghi".into(), 3);
190
        t.insert("jkl".into(), 4);
191
192
        assert_eq!(t.get("abc"), Some(&1));
193
        assert_eq!(t.get("def"), None);
194
        assert_eq!(t.get("ghi"), Some(&3));
195
        assert_eq!(t.get("jkl"), Some(&4));
196
197
        t.insert("mno".into(), 5);
198
        assert_eq!(t.get("abc"), None);
199
        assert_eq!(t.get("mno"), Some(&5));
200
    }
201
202
    #[test]
203
    fn test_removes_all_items() {
204
        let mut t = Test::new(3);
205
        t.insert("abc".into(), 1);
206
        t.insert("def".into(), 2);
207
208
        assert_eq!(t.remove("def"), Some(2));
209
        assert_eq!(t.remove("abc"), Some(1));
210
211
        t.insert("ghi".into(), 3);
212
        t.insert("jkl".into(), 4);
213
        t.insert("mno".into(), 5);
214
215
        assert_eq!(t.get("abc"), None);
216
        assert_eq!(t.get("def"), None);
217
        assert_eq!(t.get("ghi"), Some(&3));
218
        assert_eq!(t.get("jkl"), Some(&4));
219
        assert_eq!(t.get("mno"), Some(&5));
220
    }
221
222
    #[test]
223
    fn test_inserts_many_items() {
224
        let mut t = Test::new(3);
225
226
        for _ in 0..10000 {
227
            t.insert("abc".into(), 1);
228
            t.insert("def".into(), 2);
229
            t.insert("ghi".into(), 3);
230
        }
231
    }
232
233
    #[test]
234
    fn test_get_or_insert_default_and_edit_evicts_old_items_to_meet_capacity() {
235
        let mut t = Test::new(3);
236
237
        t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1);
238
        t.get_or_insert_default_and_edit("def".into(), |v| *v += 2);
239
        t.get_or_insert_default_and_edit("ghi".into(), |v| *v += 3);
240
        assert_eq!(t.get("abc"), Some(&1));
241
242
        // evicts "abc"
243
        t.get_or_insert_default_and_edit("jkl".into(), |v| *v += 4);
244
        assert_eq!(t.get("abc"), None);
245
246
        // evicts "def"
247
        t.get_or_insert_default_and_edit("abc".into(), |v| *v += 5);
248
        assert_eq!(t.get("def"), None);
249
250
        // evicts "ghi"
251
        t.get_or_insert_default_and_edit("def".into(), |v| *v += 6);
252
        assert_eq!(t.get("ghi"), None);
253
254
        assert_eq!(t.get("abc"), Some(&5));
255
        assert_eq!(t.get("def"), Some(&6));
256
        assert_eq!(t.get("ghi"), None);
257
        assert_eq!(t.get("jkl"), Some(&4));
258
    }
259
260
    #[test]
261
    fn test_get_or_insert_default_and_edit_edits_existing_item() {
262
        let mut t = Test::new(3);
263
264
        t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1);
265
        t.get_or_insert_default_and_edit("abc".into(), |v| *v += 2);
266
        t.get_or_insert_default_and_edit("abc".into(), |v| *v += 3);
267
268
        assert_eq!(t.get("abc"), Some(&6));
269
    }
270
}