1use std::collections::BTreeMap;
4
5use crate::SizeBytes;
6use crate::std_sizes::btree_heap_size;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct BookkeepingBTreeMap<K, V> {
13 map: BTreeMap<K, V>,
14
15 kv_heap_size_bytes: u64,
22}
23
24impl<K: Ord + SizeBytes, V: SizeBytes> SizeBytes for BookkeepingBTreeMap<K, V> {
25 #[inline]
26 fn heap_size_bytes(&self) -> u64 {
27 btree_heap_size(self.len(), size_of::<K>() + size_of::<V>()) + self.kv_heap_size_bytes
28 }
29}
30
31impl<K, V> Default for BookkeepingBTreeMap<K, V>
32where
33 K: Ord + SizeBytes,
34 V: SizeBytes,
35{
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl<K, V> BookkeepingBTreeMap<K, V>
42where
43 K: Ord + SizeBytes,
44 V: SizeBytes,
45{
46 #[inline]
48 pub fn new() -> Self {
49 Self {
50 map: BTreeMap::new(),
51 kv_heap_size_bytes: 0,
52 }
53 }
54
55 #[inline]
57 pub fn is_empty(&self) -> bool {
58 self.map.is_empty()
59 }
60
61 #[inline]
63 pub fn len(&self) -> usize {
64 self.map.len()
65 }
66
67 #[inline]
69 pub fn iter(&self) -> std::collections::btree_map::Iter<'_, K, V> {
70 self.map.iter()
71 }
72
73 pub fn mutate_entry(&mut self, key: K, default_value: V, mut mutator: impl FnMut(&mut V)) {
77 use std::collections::btree_map::Entry;
78
79 match self.map.entry(key) {
80 Entry::Vacant(vacant) => {
81 self.kv_heap_size_bytes += vacant.key().heap_size_bytes();
82 let value_ref = vacant.insert(default_value);
83 mutator(value_ref);
84 self.kv_heap_size_bytes += value_ref.heap_size_bytes();
85 }
86 Entry::Occupied(mut occupied) => {
87 self.kv_heap_size_bytes -= occupied.get().heap_size_bytes();
88 mutator(occupied.get_mut());
89 self.kv_heap_size_bytes += occupied.get().heap_size_bytes();
90 }
91 }
92 }
93
94 pub fn mutate_latest_at<Ret>(
99 &mut self,
100 key: &K,
101 mut mutator: impl FnMut(&K, &mut V) -> Ret,
102 ) -> Option<Ret>
103 where
104 K: Clone,
105 {
106 let (key, value) = self.map.range_mut(..=key).next_back()?;
107 self.kv_heap_size_bytes -= value.heap_size_bytes();
108 let ret = mutator(key, value);
109 self.kv_heap_size_bytes += value.heap_size_bytes();
110 Some(ret)
111 }
112
113 #[inline]
115 pub fn as_map(&self) -> &BTreeMap<K, V> {
116 &self.map
117 }
118
119 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
121 let new_key_size = key.heap_size_bytes();
122 let new_value_size = value.heap_size_bytes();
123
124 let old_value = self.map.insert(key, value);
125
126 if let Some(old_value) = &old_value {
127 self.kv_heap_size_bytes -= old_value.heap_size_bytes();
129 self.kv_heap_size_bytes += new_value_size;
130 } else {
131 self.kv_heap_size_bytes += new_key_size + new_value_size;
133 }
134
135 old_value
136 }
137
138 pub fn remove(&mut self, key: &K) -> Option<V> {
140 if let Some(value) = self.map.remove(key) {
141 self.kv_heap_size_bytes -= key.heap_size_bytes();
142 self.kv_heap_size_bytes -= value.heap_size_bytes();
143 Some(value)
144 } else {
145 None
146 }
147 }
148
149 pub fn extend<I>(&mut self, iter: I)
151 where
152 I: IntoIterator<Item = (K, V)>,
153 {
154 for (key, value) in iter {
155 self.insert(key, value);
156 }
157 }
158}
159
160impl<'a, K, V> IntoIterator for &'a BookkeepingBTreeMap<K, V>
161where
162 K: Ord + SizeBytes,
163 V: SizeBytes,
164{
165 type Item = (&'a K, &'a V);
166 type IntoIter = std::collections::btree_map::Iter<'a, K, V>;
167
168 fn into_iter(self) -> Self::IntoIter {
169 self.iter()
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn heap_size_of_map<K: Ord + SizeBytes, V: SizeBytes>(map: &BookkeepingBTreeMap<K, V>) -> u64 {
178 let entry_heap_size: u64 = map
179 .iter()
180 .map(|(k, v)| k.heap_size_bytes() + v.heap_size_bytes())
181 .sum();
182 crate::std_sizes::btree_heap_size(map.len(), size_of::<K>() + size_of::<V>())
183 + entry_heap_size
184 }
185
186 #[test]
187 fn test_multiple_entries() {
188 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
189 assert_eq!(map.heap_size_bytes(), 0);
190
191 map.insert(1, "one".to_owned());
192 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
193
194 map.insert(2, "two".to_owned());
195 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
196
197 map.insert(2, "two, but now it is different".to_owned());
198 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
199
200 map.remove(&1);
201 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
202
203 map.remove(&2);
204 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
205 assert_eq!(map.heap_size_bytes(), 0);
206 }
207
208 #[test]
209 fn test_new_and_default() {
210 let map1: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
211 let map2: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::default();
212
213 assert_eq!(map1.heap_size_bytes(), 0);
214 assert_eq!(map2.heap_size_bytes(), 0);
215 assert!(map1.is_empty());
216 assert!(map2.is_empty());
217 assert_eq!(map1.len(), 0);
218 assert_eq!(map2.len(), 0);
219 }
220
221 #[test]
222 fn test_insert_bookkeeping() {
223 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
224
225 let old = map.insert(1, "hello".to_owned());
226 assert_eq!(old, None);
227 assert_eq!(map.len(), 1);
228 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
229
230 let old = map.insert(2, "world".to_owned());
231 assert_eq!(old, None);
232 assert_eq!(map.len(), 2);
233 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
234
235 let old = map.insert(1, "hello, this is much longer!".to_owned());
236 assert_eq!(old, Some("hello".to_owned()));
237 assert_eq!(map.len(), 2);
238 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
239
240 let old = map.insert(1, "hi".to_owned());
241 assert_eq!(old, Some("hello, this is much longer!".to_owned()));
242 assert_eq!(map.len(), 2);
243 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
244 }
245
246 #[test]
247 fn test_remove_bookkeeping() {
248 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
249
250 map.insert(1, "one".to_owned());
251 map.insert(2, "two".to_owned());
252 map.insert(3, "three".to_owned());
253 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
254
255 let removed = map.remove(&2);
256 assert_eq!(removed, Some("two".to_owned()));
257 assert_eq!(map.len(), 2);
258 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
259
260 let removed = map.remove(&99);
261 assert_eq!(removed, None);
262 assert_eq!(map.len(), 2);
263 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
264
265 map.remove(&1);
266 map.remove(&3);
267 assert_eq!(map.heap_size_bytes(), 0);
268 assert!(map.is_empty());
269 }
270
271 #[test]
272 fn test_extend_bookkeeping() {
273 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
274
275 map.extend(vec![
276 (1, "one".to_owned()),
277 (2, "two".to_owned()),
278 (3, "three".to_owned()),
279 ]);
280 assert_eq!(map.len(), 3);
281 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
282
283 map.extend(vec![(2, "TWO".to_owned()), (4, "four".to_owned())]);
284 assert_eq!(map.len(), 4);
285 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
286 }
287
288 #[test]
289 fn test_mutate_entry_bookkeeping() {
290 let mut map: BookkeepingBTreeMap<u64, Vec<String>> = BookkeepingBTreeMap::new();
291
292 map.mutate_entry(1, Vec::new(), |vec| {
293 vec.push("hello".to_owned());
294 });
295 assert_eq!(map.len(), 1);
296 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
297
298 map.mutate_entry(1, Vec::new(), |vec| {
299 vec.push("world".to_owned());
300 });
301 assert_eq!(map.len(), 1);
302 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
303
304 map.mutate_entry(1, Vec::new(), |vec| {
305 vec.pop();
306 });
307 assert_eq!(map.len(), 1);
308 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
309
310 map.mutate_entry(1, Vec::new(), |vec| {
311 vec.clear();
312 });
313 assert_eq!(map.len(), 1);
314 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
315 }
316
317 #[test]
318 fn test_mutate_entry_before_bookkeeping() {
319 let mut map: BookkeepingBTreeMap<u64, Vec<String>> = BookkeepingBTreeMap::new();
320
321 map.insert(10, vec!["ten".to_owned()]);
322 map.insert(20, vec!["twenty".to_owned()]);
323 map.insert(30, vec!["thirty".to_owned()]);
324 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
325
326 let result = map.mutate_latest_at(&20, |key, vec| {
327 assert_eq!(*key, 20);
328 vec.push("added".to_owned());
329 *key
330 });
331 assert_eq!(result, Some(20));
332 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
333
334 let result = map.mutate_latest_at(&100, |key, vec| {
335 assert_eq!(*key, 30);
336 vec.clear();
337 *key
338 });
339 assert_eq!(result, Some(30));
340 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
341
342 let result = map.mutate_latest_at(&5, |_key, vec| {
343 vec.push("should not happen".to_owned());
344 });
345 assert_eq!(result, None);
346 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
347 }
348
349 #[test]
350 fn test_iter() {
351 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
352
353 map.insert(3, "three".to_owned());
354 map.insert(1, "one".to_owned());
355 map.insert(2, "two".to_owned());
356
357 let items: Vec<_> = map.iter().collect();
358 assert_eq!(items.len(), 3);
359 assert_eq!(*items[0].0, 1);
360 assert_eq!(*items[1].0, 2);
361 assert_eq!(*items[2].0, 3);
362 }
363
364 #[test]
365 fn test_into_iterator() {
366 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
367
368 map.insert(2, "two".to_owned());
369 map.insert(1, "one".to_owned());
370
371 let items: Vec<_> = (&map).into_iter().collect();
372 assert_eq!(items.len(), 2);
373 assert_eq!(*items[0].0, 1);
374 assert_eq!(*items[1].0, 2);
375 }
376
377 #[test]
378 fn test_clone() {
379 let mut map1: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
380
381 map1.insert(1, "one".to_owned());
382 map1.insert(2, "two".to_owned());
383
384 let map2 = map1.clone();
385
386 assert_eq!(map1.len(), map2.len());
387 assert_eq!(map1.heap_size_bytes(), map2.heap_size_bytes());
388 assert_eq!(map1, map2);
389 assert_eq!(map2.heap_size_bytes(), heap_size_of_map(&map2));
390 }
391
392 #[test]
393 fn test_partial_eq() {
394 let mut map1: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
395 let mut map2: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
396
397 map1.insert(1, "one".to_owned());
398 map2.insert(1, "one".to_owned());
399 assert_eq!(map1, map2);
400
401 map1.insert(2, "two".to_owned());
402 assert_ne!(map1, map2);
403
404 map2.insert(2, "TWO".to_owned());
405 assert_ne!(map1, map2);
406
407 map2.insert(2, "two".to_owned());
408 assert_eq!(map1, map2);
409 }
410
411 #[test]
412 fn test_as_map() {
413 let mut map: BookkeepingBTreeMap<u64, String> = BookkeepingBTreeMap::new();
414
415 map.insert(1, "one".to_owned());
416 map.insert(2, "two".to_owned());
417
418 let inner_map = map.as_map();
419 assert_eq!(inner_map.len(), 2);
420 assert_eq!(inner_map.get(&1), Some(&"one".to_owned()));
421 assert_eq!(inner_map.get(&2), Some(&"two".to_owned()));
422 }
423
424 #[test]
425 fn test_bookkeeping_stress() {
426 let mut map: BookkeepingBTreeMap<u64, Vec<String>> = BookkeepingBTreeMap::new();
427
428 for i in 0..100 {
429 map.insert(i, vec![format!("value_{i}")]);
430 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
431 }
432
433 for i in (0..100).step_by(5) {
434 map.mutate_entry(i, Vec::new(), |vec| {
435 vec.push(format!("extra_{i}"));
436 });
437 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
438 }
439
440 for i in (0..100).step_by(3) {
441 map.remove(&i);
442 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
443 }
444
445 assert_eq!(map.heap_size_bytes(), heap_size_of_map(&map));
446 }
447}