1use crate::SimItem;
2use core::fmt;
3use std::{
4 collections::BTreeMap,
5 sync::{Arc, RwLock, RwLockWriteGuard},
6};
7
8#[derive(Clone)]
12pub struct SimCache {
13 inner: Arc<RwLock<BTreeMap<u128, SimItem>>>,
14 capacity: usize,
15}
16
17impl fmt::Debug for SimCache {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("SimCache").finish()
20 }
21}
22
23impl Default for SimCache {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl SimCache {
30 pub fn new() -> Self {
32 Self { inner: Arc::new(RwLock::new(BTreeMap::new())), capacity: 100 }
33 }
34
35 pub fn with_capacity(capacity: usize) -> Self {
37 Self { inner: Arc::new(RwLock::new(BTreeMap::new())), capacity }
38 }
39
40 pub fn read_best(&self, n: usize) -> Vec<(u128, SimItem)> {
42 self.inner.read().unwrap().iter().rev().take(n).map(|(k, v)| (*k, v.clone())).collect()
43 }
44
45 pub fn len(&self) -> usize {
47 self.inner.read().unwrap().len()
48 }
49
50 pub fn is_empty(&self) -> bool {
52 self.inner.read().unwrap().is_empty()
53 }
54
55 pub fn get(&self, key: u128) -> Option<SimItem> {
57 self.inner.read().unwrap().get(&key).cloned()
58 }
59
60 pub fn remove(&self, key: u128) -> Option<SimItem> {
62 self.inner.write().unwrap().remove(&key)
63 }
64
65 fn add_inner(
66 guard: &mut RwLockWriteGuard<'_, BTreeMap<u128, SimItem>>,
67 mut score: u128,
68 item: SimItem,
69 capacity: usize,
70 ) {
71 while guard.contains_key(&score) && score != 0 {
73 score = score.saturating_sub(1);
74 }
75
76 if guard.len() >= capacity {
77 guard.pop_first();
79 }
80
81 guard.entry(score).or_insert(item);
82 }
83
84 pub fn add_item(&self, item: impl Into<SimItem>, basefee: u64) {
88 let item = item.into();
89
90 let score = item.calculate_total_fee(basefee);
92
93 let mut inner = self.inner.write().unwrap();
94
95 Self::add_inner(&mut inner, score, item, self.capacity);
96 }
97
98 pub fn add_items<I, Item>(&self, item: I, basefee: u64)
100 where
101 I: IntoIterator<Item = Item>,
102 Item: Into<SimItem>,
103 {
104 let iter = item.into_iter().map(|item| {
105 let item = item.into();
106 let score = item.calculate_total_fee(basefee);
107 (score, item)
108 });
109
110 let mut inner = self.inner.write().unwrap();
111
112 for (score, item) in iter {
113 Self::add_inner(&mut inner, score, item, self.capacity);
114 }
115 }
116
117 pub fn clean(&self, block_number: u64, block_timestamp: u64) {
120 let mut inner = self.inner.write().unwrap();
121
122 while inner.len() > self.capacity {
124 inner.pop_first();
125 }
126
127 inner.retain(|_, value| {
128 let SimItem::Bundle(bundle) = value else {
129 return true;
130 };
131 if bundle.bundle.block_number != block_number {
132 return false;
133 }
134 if let Some(timestamp) = bundle.min_timestamp() {
135 if timestamp > block_timestamp {
136 return false;
137 }
138 }
139 if let Some(timestamp) = bundle.max_timestamp() {
140 if timestamp < block_timestamp {
141 return false;
142 }
143 }
144 true
145 })
146 }
147
148 pub fn clear(&self) {
150 let mut inner = self.inner.write().unwrap();
151 inner.clear();
152 }
153}
154
155#[cfg(test)]
156mod test {
157 use super::*;
158 use crate::SimItem;
159
160 #[test]
161 fn test_cache() {
162 let items = vec![
163 SimItem::invalid_item_with_score(100, 1),
164 SimItem::invalid_item_with_score(100, 2),
165 SimItem::invalid_item_with_score(100, 3),
166 ];
167
168 let cache = SimCache::with_capacity(2);
169 cache.add_items(items, 0);
170
171 assert_eq!(cache.len(), 2);
172 assert_eq!(cache.get(300), Some(SimItem::invalid_item_with_score(100, 3)));
173 assert_eq!(cache.get(200), Some(SimItem::invalid_item_with_score(100, 2)));
174 assert_eq!(cache.get(100), None);
175 }
176
177 #[test]
178 fn overlap_at_zero() {
179 let items = vec![
180 SimItem::invalid_item_with_score(1, 1),
181 SimItem::invalid_item_with_score(1, 1),
182 SimItem::invalid_item_with_score(1, 1),
183 ];
184
185 let cache = SimCache::with_capacity(2);
186 cache.add_items(items, 0);
187
188 dbg!(&*cache.inner.read().unwrap());
189
190 assert_eq!(cache.len(), 2);
191 assert_eq!(cache.get(0), Some(SimItem::invalid_item_with_score(1, 1)));
192 assert_eq!(cache.get(1), Some(SimItem::invalid_item_with_score(1, 1)));
193 assert_eq!(cache.get(2), None);
194 }
195}