Skip to main content

ferroui_base/utilities/
ref_counting_small_dictionary.rs

1use super::small_dictionary::{InlineDictionary, InlineDictionaryEnumerator};
2use std::hash::Hash;
3
4/// Counts the references to keys in an [`InlineDictionary`].
5pub struct RefCountingSmallDictionary<TKey> {
6    counts: InlineDictionary<TKey, i32>,
7}
8
9impl<TKey: Clone + Eq + Hash> Default for RefCountingSmallDictionary<TKey> {
10    fn default() -> Self {
11        Self { counts: InlineDictionary::new() }
12    }
13}
14
15impl<TKey: Clone + Eq + Hash> RefCountingSmallDictionary<TKey> {
16    /// Adds a reference to the key. Returns whether it is the first one.
17    pub fn add(&mut self, key: TKey) -> bool {
18        let (cnt, exists) = self.counts.get_value_ref_or_add_default(key);
19        *cnt += 1;
20        !exists
21    }
22
23    /// Removes a reference to the key. Returns whether it was the last one.
24    ///
25    /// Panics when the key has no reference.
26    pub fn remove(&mut self, key: &TKey) -> bool {
27        let remaining = match self.counts.get_value_ref_or_null_ref(key) {
28            Some(cnt) => {
29                *cnt -= 1;
30                *cnt
31            }
32            None => panic!("Object reference not set to an instance of an object."),
33        };
34        if remaining == 0 {
35            self.counts.remove(key);
36            return true;
37        }
38
39        false
40    }
41
42    pub fn get_enumerator(&self) -> InlineDictionaryEnumerator<'_, TKey, i32> {
43        self.counts.get_enumerator()
44    }
45}
46
47impl<'a, TKey: Clone + Eq + Hash> IntoIterator for &'a RefCountingSmallDictionary<TKey> {
48    type Item = (&'a TKey, &'a i32);
49    type IntoIter = InlineDictionaryEnumerator<'a, TKey, i32>;
50
51    fn into_iter(self) -> Self::IntoIter {
52        self.get_enumerator()
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    // Not from upstream: the original has no tests of this type.
59    use super::*;
60
61    #[test]
62    fn counts_references() {
63        let mut target = RefCountingSmallDictionary::<&'static str>::default();
64
65        assert!(target.add("a"));
66        assert!(!target.add("a"));
67        assert!(target.add("b"));
68
69        let counts: Vec<(&'static str, i32)> = target.get_enumerator().map(|(key, count)| (*key, *count)).collect();
70        assert_eq!(vec![("a", 2), ("b", 1)], counts);
71
72        assert!(!target.remove(&"a"));
73        assert!(target.remove(&"a"));
74        assert!(target.remove(&"b"));
75        assert_eq!(0, target.get_enumerator().count());
76    }
77
78    #[test]
79    #[should_panic(expected = "Object reference not set to an instance of an object.")]
80    fn remove_of_a_key_without_reference_throws() {
81        let mut target = RefCountingSmallDictionary::<&'static str>::default();
82        target.remove(&"a");
83    }
84}