commonware_storage/index/
unordered.rs1use crate::{
6 index::{
7 Cursor as CursorTrait, Unordered,
8 storage::{Cursor as CursorImpl, IndexEntry, Overflow, Values, push_displaced},
9 },
10 translator::Translator,
11};
12use commonware_runtime::{
13 Metrics,
14 telemetry::metrics::{Counter, Gauge, MetricsExt as _},
15};
16use std::collections::{
17 HashMap,
18 hash_map::{Entry, OccupiedEntry, VacantEntry},
19};
20
21impl<K: Send + Sync, V: Send + Sync> IndexEntry<V> for OccupiedEntry<'_, K, V> {
23 type Key = K;
24
25 fn key(&self) -> &K {
26 OccupiedEntry::key(self)
27 }
28
29 fn get_mut(&mut self) -> &mut V {
30 self.get_mut()
31 }
32
33 fn remove(self) {
34 OccupiedEntry::remove(self);
35 }
36}
37
38pub type Cursor<'a, K, V, S> = CursorImpl<'a, K, V, OccupiedEntry<'a, K, V>, S>;
40
41pub struct Index<T: Translator, V: Send + Sync> {
48 translator: T,
49 map: HashMap<T::Key, V, T>,
50 overflow: Overflow<T::Key, V, T>,
51
52 keys: Gauge,
53 items: Gauge,
54 pruned: Counter,
55}
56
57impl<T: Translator, V: Send + Sync> Index<T, V> {
58 fn create(keys: &Gauge, items: &Gauge, vacant: VacantEntry<'_, T::Key, V>, v: V) {
60 keys.inc();
61 items.inc();
62 vacant.insert(v);
63 }
64
65 pub fn new(ctx: impl Metrics, translator: T) -> Self {
68 Self {
69 translator: translator.clone(),
70 overflow: HashMap::with_hasher(translator.clone()),
71 map: HashMap::with_hasher(translator),
72 keys: ctx.gauge("keys", "Number of translated keys in the index"),
73 items: ctx.gauge("items", "Number of items in the index"),
74 pruned: ctx.counter("pruned", "Number of items pruned"),
75 }
76 }
77
78 #[commonware_macros::stability(ALPHA)]
81 pub(crate) fn empty(&self) -> Self {
82 Self {
83 translator: self.translator.clone(),
84 overflow: HashMap::with_hasher(self.translator.clone()),
85 map: HashMap::with_hasher(self.translator.clone()),
86 keys: self.keys.clone(),
87 items: self.items.clone(),
88 pruned: self.pruned.clone(),
89 }
90 }
91
92 #[commonware_macros::stability(ALPHA)]
100 pub(crate) fn absorb(&mut self, other: Self) {
101 assert!(
102 self.map.is_empty() && self.overflow.is_empty(),
103 "absorb target must be empty"
104 );
105 self.map = other.map;
106 self.overflow = other.overflow;
107 }
108
109 #[commonware_macros::stability(ALPHA)]
111 pub(crate) fn for_each_value(&self, mut f: impl FnMut(&V)) {
112 for v in self.map.values() {
113 f(v);
114 }
115 for chain in self.overflow.values() {
116 for v in chain {
117 f(v);
118 }
119 }
120 }
121}
122
123impl<T: Translator, V: Send + Sync> super::Factory for Index<T, V> {
124 type Translator = T;
125
126 fn new(ctx: impl commonware_runtime::Metrics, translator: T) -> Self {
127 Self::new(ctx, translator)
128 }
129}
130
131impl<T: Translator, V: Send + Sync> Unordered for Index<T, V> {
132 type Value = V;
133 type Cursor<'a>
134 = Cursor<'a, T::Key, V, T>
135 where
136 Self: 'a;
137
138 fn get<'a>(&'a self, key: &[u8]) -> impl Iterator<Item = &'a V> + 'a
139 where
140 V: 'a,
141 {
142 let k = self.translator.transform(key);
143 Values::new(self.map.get(&k), &self.overflow, k)
144 }
145
146 fn get_mut<'a>(&'a mut self, key: &[u8]) -> Option<Self::Cursor<'a>> {
147 let k = self.translator.transform(key);
148 match self.map.entry(k) {
149 Entry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
150 entry,
151 &mut self.overflow,
152 &self.keys,
153 &self.items,
154 &self.pruned,
155 )),
156 Entry::Vacant(_) => None,
157 }
158 }
159
160 fn get_mut_or_insert<'a>(&'a mut self, key: &[u8], value: V) -> Option<Self::Cursor<'a>> {
161 let k = self.translator.transform(key);
162 match self.map.entry(k) {
163 Entry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
164 entry,
165 &mut self.overflow,
166 &self.keys,
167 &self.items,
168 &self.pruned,
169 )),
170 Entry::Vacant(entry) => {
171 Self::create(&self.keys, &self.items, entry, value);
172 None
173 }
174 }
175 }
176
177 fn insert(&mut self, key: &[u8], v: V) {
178 let k = self.translator.transform(key);
179 match self.map.entry(k) {
180 Entry::Occupied(mut entry) => {
181 let old = std::mem::replace(entry.get_mut(), v);
184 push_displaced(&mut self.overflow, k, old);
185 self.items.inc();
186 }
187 Entry::Vacant(entry) => {
188 Self::create(&self.keys, &self.items, entry, v);
189 }
190 }
191 }
192
193 fn insert_and_retain(&mut self, key: &[u8], value: V, should_retain: impl Fn(&V) -> bool) {
194 let k = self.translator.transform(key);
195 match self.map.entry(k) {
196 Entry::Occupied(mut entry) => {
197 #[allow(clippy::map_entry)]
199 if !self.overflow.contains_key(&k) {
200 match (should_retain(entry.get()), should_retain(&value)) {
201 (true, true) => {
203 self.overflow.insert(k, vec![value]);
204 self.items.inc();
205 }
206 (false, true) => {
208 *entry.get_mut() = value;
209 self.pruned.inc();
210 }
211 (false, false) => {
213 entry.remove();
214 self.keys.dec();
215 self.items.dec();
216 self.pruned.inc();
217 }
218 (true, false) => {}
220 }
221 return;
222 }
223
224 let mut cursor = Cursor::<'_, T::Key, V, T>::new(
226 entry,
227 &mut self.overflow,
228 &self.keys,
229 &self.items,
230 &self.pruned,
231 );
232
233 cursor.retain(&should_retain);
235
236 if should_retain(&value) {
238 cursor.insert(value);
239 }
240 }
241 Entry::Vacant(entry) => {
242 if should_retain(&value) {
244 Self::create(&self.keys, &self.items, entry, value);
245 }
246 }
247 }
248 }
249
250 fn remove(&mut self, key: &[u8]) {
251 let k = self.translator.transform(key);
252 if self.map.remove(&k).is_some() {
253 self.keys.dec();
255 self.items.dec();
256 self.pruned.inc();
257 if !self.overflow.is_empty()
258 && let Some(chain) = self.overflow.remove(&k)
259 {
260 self.items.dec_by(chain.len() as i64);
261 self.pruned.inc_by(chain.len() as u64);
262 }
263 }
264 }
265
266 #[cfg(test)]
267 fn keys(&self) -> usize {
268 self.keys.get() as usize
269 }
270
271 #[cfg(test)]
272 fn items(&self) -> usize {
273 self.items.get() as usize
274 }
275
276 #[cfg(test)]
277 fn pruned(&self) -> usize {
278 self.pruned.get() as usize
279 }
280}