opentelemetry_spanprocessor_any/sdk/trace/
evicted_hash_map.rs1use crate::{Key, KeyValue, Value};
4#[cfg(feature = "serialize")]
5use serde::{Deserialize, Serialize};
6use std::collections::hash_map::Entry;
7use std::collections::{HashMap, LinkedList};
8
9#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
12#[derive(Clone, Debug, PartialEq)]
13pub struct EvictedHashMap {
14 map: HashMap<Key, Value>,
15 evict_list: LinkedList<Key>,
16 max_len: u32,
17 dropped_count: u32,
18}
19
20impl EvictedHashMap {
21 pub fn new(max_len: u32, capacity: usize) -> Self {
23 EvictedHashMap {
24 map: HashMap::with_capacity(capacity),
25 evict_list: LinkedList::new(),
26 max_len,
27 dropped_count: 0,
28 }
29 }
30
31 pub fn insert(&mut self, item: KeyValue) {
33 let KeyValue { key, value } = item;
34 let mut already_exists = false;
35 match self.map.entry(key.clone()) {
37 Entry::Occupied(mut occupied) => {
38 occupied.insert(value);
39 already_exists = true;
40 }
41 Entry::Vacant(entry) => {
42 entry.insert(value);
43 }
44 }
45
46 if already_exists {
47 self.move_key_to_front(key);
48 } else {
49 self.evict_list.push_front(key);
51 }
52
53 if self.evict_list.len() as u32 > self.max_len {
55 self.remove_oldest();
56 self.dropped_count += 1;
57 }
58 }
59
60 pub fn len(&self) -> usize {
62 self.map.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.map.is_empty()
68 }
69
70 pub fn dropped_count(&self) -> u32 {
72 self.dropped_count
73 }
74
75 pub fn iter(&self) -> Iter<'_> {
77 Iter(self.map.iter())
78 }
79
80 pub fn get(&self, key: &Key) -> Option<&Value> {
82 self.map.get(key)
83 }
84
85 fn move_key_to_front(&mut self, key: Key) {
86 if self.evict_list.is_empty() {
87 self.evict_list.push_front(key);
89 } else if self.evict_list.front() == Some(&key) {
90 } else {
92 let key_idx = self
94 .evict_list
95 .iter()
96 .position(|k| k == &key)
97 .expect("key must exist in evicted hash map, this is a bug");
98 let mut tail = self.evict_list.split_off(key_idx);
99 let item = tail.pop_front().unwrap();
100 self.evict_list.push_front(item);
101 self.evict_list.append(&mut tail);
102 }
103 }
104
105 fn remove_oldest(&mut self) {
106 if let Some(oldest_item) = self.evict_list.pop_back() {
107 self.map.remove(&oldest_item);
108 }
109 }
110}
111
112#[derive(Debug)]
114pub struct IntoIter(std::collections::hash_map::IntoIter<Key, Value>);
115
116impl Iterator for IntoIter {
117 type Item = (Key, Value);
118
119 fn next(&mut self) -> Option<Self::Item> {
120 self.0.next()
121 }
122}
123
124impl IntoIterator for EvictedHashMap {
125 type Item = (Key, Value);
126 type IntoIter = IntoIter;
127
128 fn into_iter(self) -> Self::IntoIter {
129 IntoIter(self.map.into_iter())
130 }
131}
132
133impl<'a> IntoIterator for &'a EvictedHashMap {
134 type Item = (&'a Key, &'a Value);
135 type IntoIter = Iter<'a>;
136
137 fn into_iter(self) -> Self::IntoIter {
138 Iter(self.map.iter())
139 }
140}
141
142#[derive(Debug)]
144pub struct Iter<'a>(std::collections::hash_map::Iter<'a, Key, Value>);
145
146impl<'a> Iterator for Iter<'a> {
147 type Item = (&'a Key, &'a Value);
148
149 fn next(&mut self) -> Option<Self::Item> {
150 self.0.next()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use std::collections::HashSet;
158
159 #[test]
160 fn insert_over_capacity_test() {
161 let max_len = 10;
162 let mut map = EvictedHashMap::new(max_len, max_len as usize);
163
164 for i in 0..=max_len {
165 map.insert(Key::new(i.to_string()).bool(true))
166 }
167
168 assert_eq!(map.dropped_count, 1);
169 assert_eq!(map.len(), max_len as usize);
170 assert_eq!(
171 map.map.keys().cloned().collect::<HashSet<_>>(),
172 (1..=max_len)
173 .map(|i| Key::new(i.to_string()))
174 .collect::<HashSet<_>>()
175 );
176 }
177}