Skip to main content

iddqd/id_hash_map/
entry.rs

1use super::{IdHashItem, IdHashMap, RefMut};
2use crate::{
3    DefaultHashBuilder,
4    support::{
5        ItemIndex,
6        alloc::{Allocator, Global},
7        map_hash::MapHash,
8    },
9};
10use core::{fmt, hash::BuildHasher};
11
12/// An implementation of the Entry API for [`IdHashMap`].
13pub enum Entry<'a, T: IdHashItem, S = DefaultHashBuilder, A: Allocator = Global>
14{
15    /// A vacant entry.
16    Vacant(VacantEntry<'a, T, S, A>),
17    /// An occupied entry.
18    Occupied(OccupiedEntry<'a, T, S, A>),
19}
20
21impl<'a, T: IdHashItem, S, A: Allocator> fmt::Debug for Entry<'a, T, S, A> {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Entry::Vacant(entry) => {
25                f.debug_tuple("Vacant").field(entry).finish()
26            }
27            Entry::Occupied(entry) => {
28                f.debug_tuple("Occupied").field(entry).finish()
29            }
30        }
31    }
32}
33
34impl<'a, T: IdHashItem, S: Clone + BuildHasher, A: Allocator>
35    Entry<'a, T, S, A>
36{
37    /// Ensures a value is in the entry by inserting the default if empty, and
38    /// returns a mutable reference to the value in the entry.
39    ///
40    /// # Panics
41    ///
42    /// Panics if the key hashes to a different value than the one passed
43    /// into [`IdHashMap::entry`].
44    #[inline]
45    pub fn or_insert(self, default: T) -> RefMut<'a, T, S> {
46        match self {
47            Entry::Occupied(entry) => entry.into_mut(),
48            Entry::Vacant(entry) => entry.insert(default),
49        }
50    }
51
52    /// Ensures a value is in the entry by inserting the result of the default
53    /// function if empty, and returns a mutable reference to the value in the
54    /// entry.
55    ///
56    /// # Panics
57    ///
58    /// Panics if the key hashes to a different value than the one passed
59    /// into [`IdHashMap::entry`].
60    #[inline]
61    pub fn or_insert_with<F: FnOnce() -> T>(
62        self,
63        default: F,
64    ) -> RefMut<'a, T, S> {
65        match self {
66            Entry::Occupied(entry) => entry.into_mut(),
67            Entry::Vacant(entry) => entry.insert(default()),
68        }
69    }
70
71    /// Provides in-place mutable access to an occupied entry before any
72    /// potential inserts into the map.
73    #[inline]
74    pub fn and_modify<F>(self, f: F) -> Self
75    where
76        F: FnOnce(RefMut<'_, T, S>),
77    {
78        match self {
79            Entry::Occupied(mut entry) => {
80                f(entry.get_mut());
81                Entry::Occupied(entry)
82            }
83            Entry::Vacant(entry) => Entry::Vacant(entry),
84        }
85    }
86}
87
88/// A vacant entry.
89pub struct VacantEntry<
90    'a,
91    T: IdHashItem,
92    S = DefaultHashBuilder,
93    A: Allocator = Global,
94> {
95    map: &'a mut IdHashMap<T, S, A>,
96    hash: MapHash,
97}
98
99impl<'a, T: IdHashItem, S, A: Allocator> fmt::Debug
100    for VacantEntry<'a, T, S, A>
101{
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        f.debug_struct("VacantEntry")
104            .field("hash", &self.hash)
105            .finish_non_exhaustive()
106    }
107}
108
109impl<'a, T: IdHashItem, S: Clone + BuildHasher, A: Allocator>
110    VacantEntry<'a, T, S, A>
111{
112    pub(super) fn new(map: &'a mut IdHashMap<T, S, A>, hash: MapHash) -> Self {
113        VacantEntry { map, hash }
114    }
115
116    /// Sets the entry to a new value, returning a mutable reference to the
117    /// value.
118    pub fn insert(self, value: T) -> RefMut<'a, T, S> {
119        let map = self.map;
120        let state = &map.tables.state;
121        if !self.hash.is_same_hash(state, value.key()) {
122            panic!("key hashes do not match");
123        }
124        let Ok(index) = map.insert_unique_impl(value) else {
125            panic!("key already present in map");
126        };
127        map.get_by_index_mut(index).expect("index is known to be valid")
128    }
129
130    /// Sets the entry to a new value without checking for duplicates or
131    /// recomputing its key hash.
132    ///
133    /// Only call this on a vacant entry obtained from `value.key()`. Unlike
134    /// `insert`, this method does not validate the hash or recheck uniqueness.
135    #[inline]
136    pub(super) fn insert_known_unique(self, value: T) {
137        let map = self.map;
138        map.try_reserve_insert_overwrite_commit()
139            .expect("reserved space successfully");
140        let next_index = map.items.assert_can_grow().insert(value);
141        map.tables
142            .key_to_item
143            .insert_prehashed_unchecked(self.hash, next_index);
144    }
145
146    /// Sets the value of the entry, and returns an `OccupiedEntry`.
147    #[inline]
148    pub fn insert_entry(self, value: T) -> OccupiedEntry<'a, T, S, A> {
149        let state = &self.map.tables.state;
150        if !self.hash.is_same_hash(state, value.key()) {
151            panic!("key hashes do not match");
152        }
153        let Ok(index) = self.map.insert_unique_impl(value) else {
154            panic!("key already present in map");
155        };
156        OccupiedEntry::new(self.map, index)
157    }
158}
159
160/// A view into an occupied entry in an [`IdHashMap`]. Part of the [`Entry`]
161/// enum.
162pub struct OccupiedEntry<
163    'a,
164    T: IdHashItem,
165    S = DefaultHashBuilder,
166    A: Allocator = Global,
167> {
168    map: &'a mut IdHashMap<T, S, A>,
169    // index is a valid index into the map's internal hash table.
170    index: ItemIndex,
171}
172
173impl<'a, T: IdHashItem, S, A: Allocator> fmt::Debug
174    for OccupiedEntry<'a, T, S, A>
175{
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        f.debug_struct("OccupiedEntry")
178            .field("index", &self.index)
179            .finish_non_exhaustive()
180    }
181}
182
183impl<'a, T: IdHashItem, S: Clone + BuildHasher, A: Allocator>
184    OccupiedEntry<'a, T, S, A>
185{
186    pub(super) fn new(
187        map: &'a mut IdHashMap<T, S, A>,
188        index: ItemIndex,
189    ) -> Self {
190        OccupiedEntry { map, index }
191    }
192
193    /// Gets a reference to the value.
194    ///
195    /// If you need a reference to `T` that may outlive the destruction of the
196    /// `Entry` value, see [`into_ref`](Self::into_ref).
197    pub fn get(&self) -> &T {
198        self.map.get_by_index(self.index).expect("index is known to be valid")
199    }
200
201    /// Gets a mutable reference to the value.
202    ///
203    /// If you need a reference to `T` that may outlive the destruction of the
204    /// `Entry` value, see [`into_mut`](Self::into_mut).
205    pub fn get_mut(&mut self) -> RefMut<'_, T, S> {
206        self.map
207            .get_by_index_mut(self.index)
208            .expect("index is known to be valid")
209    }
210
211    /// Converts self into a reference to the value.
212    ///
213    /// If you need multiple references to the `OccupiedEntry`, see
214    /// [`get`](Self::get).
215    pub fn into_ref(self) -> &'a T {
216        self.map.get_by_index(self.index).expect("index is known to be valid")
217    }
218
219    /// Converts self into a mutable reference to the value.
220    ///
221    /// If you need multiple references to the `OccupiedEntry`, see
222    /// [`get_mut`](Self::get_mut).
223    pub fn into_mut(self) -> RefMut<'a, T, S> {
224        self.map
225            .get_by_index_mut(self.index)
226            .expect("index is known to be valid")
227    }
228
229    /// Sets the entry to a new value, returning the old value.
230    ///
231    /// # Panics
232    ///
233    /// Panics if `value.key()` is different from the key of the entry.
234    pub fn insert(&mut self, value: T) -> T {
235        // Note that `replace_at_index` panics if the keys don't match.
236        self.map.replace_at_index(self.index, value)
237    }
238
239    /// Takes ownership of the value from the map.
240    pub fn remove(self) -> T {
241        self.map
242            .remove_by_index(self.index)
243            .expect("index is known to be valid")
244    }
245}