1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use alloc::collections::hash_map::{Entry, HashMap};
6use core::hash::Hash;
7use core::num::NonZeroUsize;
89/// The result of inserting an element into a [`RefCountedHashMap`].
10#[derive(Debug, Eq, PartialEq)]
11pub enum InsertResult<O> {
12/// The key was not previously in the map, so it was inserted.
13Inserted(O),
14/// The key was already in the map, so we incremented the entry's reference
15 /// count.
16AlreadyPresent,
17}
1819/// The result of removing an entry from a [`RefCountedHashMap`].
20#[derive(Debug, Eq, PartialEq)]
21pub enum RemoveResult<V> {
22/// The reference count reached 0, so the entry was removed.
23Removed(V),
24/// The reference count did not reach 0, so the entry still exists in the map.
25StillPresent,
26/// The key was not in the map.
27NotPresent,
28}
2930/// A [`HashMap`] which keeps a reference count for each entry.
31#[derive(Debug)]
32pub struct RefCountedHashMap<K, V> {
33 inner: HashMap<K, (NonZeroUsize, V)>,
34}
3536impl<K, V> Default for RefCountedHashMap<K, V> {
37fn default() -> RefCountedHashMap<K, V> {
38 RefCountedHashMap { inner: HashMap::default() }
39 }
40}
4142impl<K: Eq + Hash, V> RefCountedHashMap<K, V> {
43/// Increments the reference count of the entry with the given key.
44 ///
45 /// If the key isn't in the map, the given function is called to create its
46 /// associated value.
47pub fn insert_with<O, F: FnOnce() -> (V, O)>(&mut self, key: K, f: F) -> InsertResult<O> {
48match self.inner.entry(key) {
49 Entry::Occupied(mut entry) => {
50let (refcnt, _): &mut (NonZeroUsize, V) = entry.get_mut();
51*refcnt = refcnt.checked_add(1).unwrap();
52 InsertResult::AlreadyPresent
53 }
54 Entry::Vacant(entry) => {
55let (value, output) = f();
56let _: &mut (NonZeroUsize, V) =
57 entry.insert((NonZeroUsize::new(1).unwrap(), value));
58 InsertResult::Inserted(output)
59 }
60 }
61 }
6263/// Decrements the reference count of the entry with the given key.
64 ///
65 /// If the reference count reaches 0, the entry will be removed and its
66 /// value returned.
67pub fn remove(&mut self, key: K) -> RemoveResult<V> {
68match self.inner.entry(key) {
69 Entry::Vacant(_) => RemoveResult::NotPresent,
70 Entry::Occupied(mut entry) => {
71let (refcnt, _): &mut (NonZeroUsize, V) = entry.get_mut();
72match NonZeroUsize::new(refcnt.get() - 1) {
73None => {
74let (_, value): (NonZeroUsize, V) = entry.remove();
75 RemoveResult::Removed(value)
76 }
77Some(new_refcnt) => {
78*refcnt = new_refcnt;
79 RemoveResult::StillPresent
80 }
81 }
82 }
83 }
84 }
8586/// Returns `true` if the map contains a value for the specified key.
87pub fn contains_key(&self, key: &K) -> bool {
88self.inner.contains_key(key)
89 }
9091/// Returns a reference to the value corresponding to the key.
92pub fn get(&self, key: &K) -> Option<&V> {
93self.inner.get(key).map(|(_, value)| value)
94 }
9596/// Returns a mutable reference to the value corresponding to the key.
97pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
98self.inner.get_mut(key).map(|(_, value)| value)
99 }
100101/// An iterator visiting all key-value pairs in arbitrary order, with
102 /// mutable references to the values.
103pub fn iter_mut<'a>(&'a mut self) -> impl 'a + Iterator<Item = (&'a K, &'a mut V)> {
104self.inner.iter_mut().map(|(key, (_, value))| (key, value))
105 }
106107/// An iterator visiting all key-value pairs in arbitrary order, with
108 /// non-mutable references to the values.
109pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = (&'a K, &'a V)> + Clone {
110self.inner.iter().map(|(key, (_, value))| (key, value))
111 }
112113/// An iterator visiting all keys in arbitrary order with the reference
114 /// count for each key.
115pub fn iter_ref_counts<'a>(
116&'a self,
117 ) -> impl 'a + Iterator<Item = (&'a K, &'a NonZeroUsize)> + Clone {
118self.inner.iter().map(|(key, (count, _))| (key, count))
119 }
120121/// Returns whether the map is empty.
122pub fn is_empty(&self) -> bool {
123self.inner.is_empty()
124 }
125}
126127/// A [`RefCountedHashMap`] where the value is `()`.
128#[derive(Debug)]
129pub struct RefCountedHashSet<T> {
130 inner: RefCountedHashMap<T, ()>,
131}
132133impl<T> Default for RefCountedHashSet<T> {
134fn default() -> RefCountedHashSet<T> {
135 RefCountedHashSet { inner: RefCountedHashMap::default() }
136 }
137}
138139impl<T: Eq + Hash> RefCountedHashSet<T> {
140/// Increments the reference count of the given value.
141pub fn insert(&mut self, value: T) -> InsertResult<()> {
142self.inner.insert_with(value, || ((), ()))
143 }
144145/// Decrements the reference count of the given value.
146 ///
147 /// If the reference count reaches 0, the value will be removed from the
148 /// set.
149pub fn remove(&mut self, value: T) -> RemoveResult<()> {
150self.inner.remove(value)
151 }
152153/// Returns `true` if the set contains the given value.
154pub fn contains(&self, value: &T) -> bool {
155self.inner.contains_key(value)
156 }
157158/// Returns the number of values in the set.
159pub fn len(&self) -> usize {
160self.inner.inner.len()
161 }
162163/// Iterates over values and reference counts.
164pub fn iter_counts(&self) -> impl Iterator<Item = (&'_ T, NonZeroUsize)> + '_ {
165self.inner.inner.iter().map(|(key, (count, ()))| (key, *count))
166 }
167}
168169impl<T: Eq + Hash> core::iter::FromIterator<T> for RefCountedHashSet<T> {
170fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
171 iter.into_iter().fold(Self::default(), |mut set, t| {
172let _: InsertResult<()> = set.insert(t);
173 set
174 })
175 }
176}
177178#[cfg(test)]
179mod test {
180use super::*;
181182#[test]
183fn test_ref_counted_hash_map() {
184let mut map = RefCountedHashMap::<&str, ()>::default();
185let key = "key";
186187// Test refcounts 1 and 2. The behavioral difference is that testing
188 // only with a refcount of 1 doesn't exercise the refcount incrementing
189 // functionality - it only exercises the functionality of initializing a
190 // new entry with a refcount of 1.
191for refcount in 1..=2 {
192assert!(!map.contains_key(&key));
193194// Insert an entry for the first time, initializing the refcount to
195 // 1.
196assert_eq!(map.insert_with(key, || ((), ())), InsertResult::Inserted(()));
197assert!(map.contains_key(&key));
198 assert_refcount(&map, key, 1, "after initial insert");
199200// Increase the refcount to `refcount`.
201for i in 1..refcount {
202// Since the refcount starts at 1, the entry is always already
203 // in the map.
204assert_eq!(map.insert_with(key, || ((), ())), InsertResult::AlreadyPresent);
205assert!(map.contains_key(&key));
206 assert_refcount(&map, key, i + 1, "after subsequent insert");
207 }
208209// Decrement the refcount to 1.
210for i in 1..refcount {
211// Since we don't decrement the refcount past 1, the entry is
212 // always still present.
213assert_eq!(map.remove(key), RemoveResult::StillPresent);
214assert!(map.contains_key(&key));
215 assert_refcount(&map, key, refcount - i, "after decrement refcount");
216 }
217218 assert_refcount(&map, key, 1, "before entry removed");
219// Remove the entry when the refcount is 1.
220assert_eq!(map.remove(key), RemoveResult::Removed(()));
221assert!(!map.contains_key(&key));
222223// Try to remove an entry that no longer exists.
224assert_eq!(map.remove(key), RemoveResult::NotPresent);
225 }
226 }
227228fn assert_refcount(
229 map: &RefCountedHashMap<&str, ()>,
230 key: &str,
231 expected_refcount: usize,
232 context: &str,
233 ) {
234let (actual_refcount, _value) =
235 map.inner.get(key).unwrap_or_else(|| panic!("refcount should be non-zero {}", context));
236assert_eq!(actual_refcount.get(), expected_refcount);
237 }
238}