iddqd/id_hash_map/entry.rs
1use super::{IdHashItem, IdHashMap, RefMut};
2use crate::{
3 DefaultHashBuilder,
4 support::{
5 alloc::{Allocator, Global},
6 borrow::DormantMutRef,
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: DormantMutRef<'a, IdHashMap<T, S, A>>,
96 hash: MapHash<S>,
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) unsafe fn new(
113 map: DormantMutRef<'a, IdHashMap<T, S, A>>,
114 hash: MapHash<S>,
115 ) -> Self {
116 VacantEntry { map, hash }
117 }
118
119 /// Sets the entry to a new value, returning a mutable reference to the
120 /// value.
121 pub fn insert(self, value: T) -> RefMut<'a, T, S> {
122 if !self.hash.is_same_hash(value.key()) {
123 panic!("key hashes do not match");
124 }
125
126 // SAFETY: The safety assumption behind `Self::new` guarantees that the
127 // original reference to the map is not used at this point.
128 let map = unsafe { self.map.awaken() };
129 let Ok(index) = map.insert_unique_impl(value) else {
130 panic!("key already present in map");
131 };
132 map.get_by_index_mut(index).expect("index is known to be valid")
133 }
134
135 /// Sets the value of the entry, and returns an `OccupiedEntry`.
136 #[inline]
137 pub fn insert_entry(mut self, value: T) -> OccupiedEntry<'a, T, S, A> {
138 if !self.hash.is_same_hash(value.key()) {
139 panic!("key hashes do not match");
140 }
141
142 let index = {
143 // SAFETY: The safety assumption behind `Self::new` guarantees that the
144 // original reference to the map is not used at this point.
145 let map = unsafe { self.map.reborrow() };
146 let Ok(index) = map.insert_unique_impl(value) else {
147 panic!("key already present in map");
148 };
149 index
150 };
151
152 // SAFETY: map, as well as anything that was borrowed from it, is
153 // dropped once the above block exits.
154 unsafe { OccupiedEntry::new(self.map, index) }
155 }
156}
157
158/// A view into an occupied entry in an [`IdHashMap`]. Part of the [`Entry`]
159/// enum.
160pub struct OccupiedEntry<
161 'a,
162 T: IdHashItem,
163 S = DefaultHashBuilder,
164 A: Allocator = Global,
165> {
166 map: DormantMutRef<'a, IdHashMap<T, S, A>>,
167 // index is a valid index into the map's internal hash table.
168 index: usize,
169}
170
171impl<'a, T: IdHashItem, S, A: Allocator> fmt::Debug
172 for OccupiedEntry<'a, T, S, A>
173{
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 f.debug_struct("OccupiedEntry")
176 .field("index", &self.index)
177 .finish_non_exhaustive()
178 }
179}
180
181impl<'a, T: IdHashItem, S: Clone + BuildHasher, A: Allocator>
182 OccupiedEntry<'a, T, S, A>
183{
184 /// # Safety
185 ///
186 /// After self is created, the original reference created by
187 /// `DormantMutRef::new` must not be used.
188 pub(super) unsafe fn new(
189 map: DormantMutRef<'a, IdHashMap<T, S, A>>,
190 index: usize,
191 ) -> Self {
192 OccupiedEntry { map, index }
193 }
194
195 /// Gets a reference to the value.
196 ///
197 /// If you need a reference to `T` that may outlive the destruction of the
198 /// `Entry` value, see [`into_ref`](Self::into_ref).
199 pub fn get(&self) -> &T {
200 // SAFETY: The safety assumption behind `Self::new` guarantees that the
201 // original reference to the map is not used at this point.
202 unsafe { self.map.reborrow_shared() }
203 .get_by_index(self.index)
204 .expect("index is known to be valid")
205 }
206
207 /// Gets a mutable reference to the value.
208 ///
209 /// If you need a reference to `T` that may outlive the destruction of the
210 /// `Entry` value, see [`into_mut`](Self::into_mut).
211 pub fn get_mut(&mut self) -> RefMut<'_, T, S> {
212 // SAFETY: The safety assumption behind `Self::new` guarantees that the
213 // original reference to the map is not used at this point.
214 unsafe { self.map.reborrow() }
215 .get_by_index_mut(self.index)
216 .expect("index is known to be valid")
217 }
218
219 /// Converts self into a reference to the value.
220 ///
221 /// If you need multiple references to the `OccupiedEntry`, see
222 /// [`get`](Self::get).
223 pub fn into_ref(self) -> &'a T {
224 // SAFETY: The safety assumption behind `Self::new` guarantees that the
225 // original reference to the map is not used at this point.
226 unsafe { self.map.awaken() }
227 .get_by_index(self.index)
228 .expect("index is known to be valid")
229 }
230
231 /// Converts self into a mutable reference to the value.
232 ///
233 /// If you need multiple references to the `OccupiedEntry`, see
234 /// [`get_mut`](Self::get_mut).
235 pub fn into_mut(self) -> RefMut<'a, T, S> {
236 // SAFETY: The safety assumption behind `Self::new` guarantees that the
237 // original reference to the map is not used at this point.
238 unsafe { self.map.awaken() }
239 .get_by_index_mut(self.index)
240 .expect("index is known to be valid")
241 }
242
243 /// Sets the entry to a new value, returning the old value.
244 ///
245 /// # Panics
246 ///
247 /// Panics if `value.key()` is different from the key of the entry.
248 pub fn insert(&mut self, value: T) -> T {
249 // SAFETY: The safety assumption behind `Self::new` guarantees that the
250 // original reference to the map is not used at this point.
251 //
252 // Note that `replace_at_index` panics if the keys don't match.
253 unsafe { self.map.reborrow() }.replace_at_index(self.index, value)
254 }
255
256 /// Takes ownership of the value from the map.
257 pub fn remove(mut self) -> T {
258 // SAFETY: The safety assumption behind `Self::new` guarantees that the
259 // original reference to the map is not used at this point.
260 unsafe { self.map.reborrow() }
261 .remove_by_index(self.index)
262 .expect("index is known to be valid")
263 }
264}