iddqd/bi_hash_map/entry.rs
1use super::{BiHashItem, BiHashMap, RefMut, entry_indexes::EntryIndexes};
2use crate::{
3 DefaultHashBuilder,
4 support::{
5 alloc::{Allocator, Global},
6 map_hash::MapHash,
7 },
8};
9use alloc::vec::Vec;
10use core::{fmt, hash::BuildHasher};
11
12/// An implementation of the Entry API for [`BiHashMap`].
13///
14/// # Differences from single-key entries
15///
16/// The shape of this type differs from those provided for the other map types,
17/// because it is possible for one of the two keys provided to correspond to an
18/// existing entry, while the other does not.
19///
20/// [`VacantEntry`] corresponds to situations where neither key is present. To
21/// insert an entry corresponding to the two keys, use [`VacantEntry::insert`].
22///
23/// [`OccupiedEntry`] represents situations where either the keys correspond to
24/// different entries, or where only one of the keys is present. It provides the
25/// following methods:
26///
27/// * [`OccupiedEntry::is_unique`] and [`OccupiedEntry::is_non_unique`] return
28/// `true` if the keys correspond to a unique or duplicate entry in the map,
29/// respectively.
30/// * [`OccupiedEntry::get`] returns an [`OccupiedEntryRef`] enum that can be
31/// matched on.
32/// * [`OccupiedEntryRef::as_unique`] returns the unique entry, if one exists.
33/// * [`OccupiedEntryRef::by_key1`] and [`OccupiedEntryRef::by_key2`] return the
34/// entry corresponding to the given key, if one exists.
35/// * Similarly, [`OccupiedEntry::get_mut`] returns an [`OccupiedEntryMut`] enum
36/// that can be matched on.
37/// * [`OccupiedEntryMut::as_unique`] returns a mutable reference to the unique
38/// entry, if one exists.
39/// * [`OccupiedEntryMut::by_key1`] and [`OccupiedEntryMut::by_key2`] return a
40/// mutable reference to the entry corresponding to the given key, if one
41/// exists.
42///
43/// # Examples
44///
45/// ```
46/// # #[cfg(feature = "default-hasher")] {
47/// use iddqd::{BiHashItem, BiHashMap, bi_hash_map, bi_upcast};
48///
49/// #[derive(Debug, PartialEq, Eq)]
50/// struct Item {
51/// id: u32,
52/// name: String,
53/// value: i32,
54/// }
55///
56/// impl BiHashItem for Item {
57/// type K1<'a> = u32;
58/// type K2<'a> = &'a str;
59///
60/// fn key1(&self) -> Self::K1<'_> {
61/// self.id
62/// }
63/// fn key2(&self) -> Self::K2<'_> {
64/// &self.name
65/// }
66/// bi_upcast!();
67/// }
68///
69/// let mut map = BiHashMap::new();
70/// map.insert_unique(Item { id: 1, name: "foo".to_string(), value: 42 })
71/// .unwrap();
72///
73/// // Get an existing entry. Both keys point to the same item, so the
74/// // entry is unique.
75/// match map.entry(1, "foo") {
76/// bi_hash_map::Entry::Occupied(entry) => {
77/// assert!(entry.is_unique());
78/// assert_eq!(entry.get().as_unique().unwrap().value, 42);
79/// }
80/// bi_hash_map::Entry::Vacant(_) => panic!("Should be occupied"),
81/// }
82///
83/// // Try to get a non-existing entry.
84/// match map.entry(2, "bar") {
85/// bi_hash_map::Entry::Occupied(_) => panic!("Should be vacant"),
86/// bi_hash_map::Entry::Vacant(entry) => {
87/// entry.insert(Item { id: 2, name: "bar".to_string(), value: 99 });
88/// }
89/// }
90///
91/// assert_eq!(map.len(), 2);
92///
93/// // An entry is non-unique when its two keys point to different items.
94/// // Here, id 1 belongs to "foo" but name "bar" belongs to id 2.
95/// match map.entry(1, "bar") {
96/// bi_hash_map::Entry::Occupied(entry) => {
97/// assert!(entry.is_non_unique());
98/// let entry_ref = entry.get();
99/// assert_eq!(entry_ref.by_key1().unwrap().name, "foo");
100/// assert_eq!(entry_ref.by_key2().unwrap().id, 2);
101/// assert_eq!(entry_ref.as_unique(), None);
102/// }
103/// bi_hash_map::Entry::Vacant(_) => panic!("Should be occupied"),
104/// }
105///
106/// // An entry is also non-unique when only one of its keys is present.
107/// match map.entry(1, "nonexistent") {
108/// bi_hash_map::Entry::Occupied(mut entry) => {
109/// assert!(entry.is_non_unique());
110/// let entry_ref = entry.get();
111/// assert_eq!(entry_ref.by_key1().unwrap().id, 1);
112/// assert_eq!(entry_ref.by_key2(), None);
113///
114/// // Inserting overwrites whichever items the keys matched,
115/// // returning them. Only id 1 ("foo") was present, so it alone
116/// // is returned.
117/// let replaced = entry.insert(Item {
118/// id: 1,
119/// name: "nonexistent".to_string(),
120/// value: 7,
121/// });
122/// assert_eq!(replaced.len(), 1);
123/// assert_eq!(replaced[0].name, "foo");
124///
125/// // The entry is now unique: both keys point to the new item.
126/// assert!(entry.is_unique());
127/// assert_eq!(entry.get().as_unique().unwrap().value, 7);
128/// }
129/// bi_hash_map::Entry::Vacant(_) => panic!("Should be occupied"),
130/// }
131///
132/// // "foo" was overwritten in place, so the map still holds two items.
133/// assert_eq!(map.get1(&1).unwrap().name, "nonexistent");
134/// assert_eq!(map.get2(&"foo"), None);
135/// assert_eq!(map.len(), 2);
136/// # }
137/// ```
138pub enum Entry<'a, T: BiHashItem, S = DefaultHashBuilder, A: Allocator = Global>
139{
140 /// A vacant entry: none of the provided keys are present.
141 Vacant(VacantEntry<'a, T, S, A>),
142 /// An occupied entry where at least one of the keys is present in the map.
143 Occupied(OccupiedEntry<'a, T, S, A>),
144}
145
146impl<'a, T: BiHashItem, S, A: Allocator> fmt::Debug for Entry<'a, T, S, A> {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self {
149 Entry::Vacant(entry) => {
150 f.debug_tuple("Vacant").field(entry).finish()
151 }
152 Entry::Occupied(entry) => {
153 f.debug_tuple("Occupied").field(entry).finish()
154 }
155 }
156 }
157}
158
159impl<'a, T: BiHashItem, S: Clone + BuildHasher, A: Allocator>
160 Entry<'a, T, S, A>
161{
162 /// Ensures a value is in the entry by inserting the default if empty, and
163 /// returns a mutable reference to the value in the entry.
164 ///
165 /// # Panics
166 ///
167 /// Panics if the key hashes to a different value than the one passed
168 /// into [`BiHashMap::entry`].
169 #[inline]
170 pub fn or_insert(self, default: T) -> OccupiedEntryMut<'a, T, S> {
171 match self {
172 Entry::Occupied(entry) => entry.into_mut(),
173 Entry::Vacant(entry) => {
174 OccupiedEntryMut::Unique(entry.insert(default))
175 }
176 }
177 }
178
179 /// Ensures a value is in the entry by inserting the result of the default
180 /// function if empty, and returns a mutable reference to the value in the
181 /// entry.
182 ///
183 /// # Panics
184 ///
185 /// Panics if the key hashes to a different value than the one passed
186 /// into [`BiHashMap::entry`].
187 #[inline]
188 pub fn or_insert_with<F: FnOnce() -> T>(
189 self,
190 default: F,
191 ) -> OccupiedEntryMut<'a, T, S> {
192 match self {
193 Entry::Occupied(entry) => entry.into_mut(),
194 Entry::Vacant(entry) => {
195 OccupiedEntryMut::Unique(entry.insert(default()))
196 }
197 }
198 }
199
200 /// Provides in-place mutable access to occupied entries before any
201 /// potential inserts into the map.
202 ///
203 /// `F` is called for each entry that matches the provided keys.
204 #[inline]
205 pub fn and_modify<F>(self, f: F) -> Self
206 where
207 F: FnMut(RefMut<'_, T, S>),
208 {
209 match self {
210 Entry::Occupied(mut entry) => {
211 entry.get_mut().for_each(f);
212 Entry::Occupied(entry)
213 }
214 Entry::Vacant(entry) => Entry::Vacant(entry),
215 }
216 }
217}
218
219/// A vacant entry.
220pub struct VacantEntry<
221 'a,
222 T: BiHashItem,
223 S = DefaultHashBuilder,
224 A: Allocator = Global,
225> {
226 map: &'a mut BiHashMap<T, S, A>,
227 hashes: [MapHash; 2],
228}
229
230impl<'a, T: BiHashItem, S, A: Allocator> fmt::Debug
231 for VacantEntry<'a, T, S, A>
232{
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 f.debug_struct("VacantEntry")
235 .field("hashes", &self.hashes)
236 .finish_non_exhaustive()
237 }
238}
239
240impl<'a, T: BiHashItem, S: Clone + BuildHasher, A: Allocator>
241 VacantEntry<'a, T, S, A>
242{
243 pub(super) fn new(
244 map: &'a mut BiHashMap<T, S, A>,
245 hashes: [MapHash; 2],
246 ) -> Self {
247 VacantEntry { map, hashes }
248 }
249
250 /// Sets the entry to a new value, returning a mutable reference to the
251 /// value.
252 pub fn insert(self, value: T) -> RefMut<'a, T, S> {
253 let map = self.map;
254 let state = &map.tables.state;
255 if !self.hashes[0].is_same_hash(state, value.key1()) {
256 panic!("key1 hashes do not match");
257 }
258 if !self.hashes[1].is_same_hash(state, value.key2()) {
259 panic!("key2 hashes do not match");
260 }
261 let Ok(index) = map.insert_unique_impl(value) else {
262 panic!("key already present in map");
263 };
264 map.get_by_index_mut(index).expect("index is known to be valid")
265 }
266
267 /// Sets the value of the entry, and returns an `OccupiedEntry`.
268 #[inline]
269 pub fn insert_entry(self, value: T) -> OccupiedEntry<'a, T, S, A> {
270 let state = &self.map.tables.state;
271 if !self.hashes[0].is_same_hash(state, value.key1()) {
272 panic!("key1 hashes do not match");
273 }
274 if !self.hashes[1].is_same_hash(state, value.key2()) {
275 panic!("key2 hashes do not match");
276 }
277 let Ok(index) = self.map.insert_unique_impl(value) else {
278 panic!("key already present in map");
279 };
280 OccupiedEntry::new(self.map, EntryIndexes::Unique(index))
281 }
282}
283
284/// A view into an occupied entry in a [`BiHashMap`]. Part of the [`Entry`]
285/// enum.
286pub struct OccupiedEntry<
287 'a,
288 T: BiHashItem,
289 S = DefaultHashBuilder,
290 A: Allocator = Global,
291> {
292 map: &'a mut BiHashMap<T, S, A>,
293 indexes: EntryIndexes,
294}
295
296impl<'a, T: BiHashItem, S, A: Allocator> fmt::Debug
297 for OccupiedEntry<'a, T, S, A>
298{
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 f.debug_struct("OccupiedEntry")
301 .field("indexes", &self.indexes)
302 .finish_non_exhaustive()
303 }
304}
305
306impl<'a, T: BiHashItem, S: Clone + BuildHasher, A: Allocator>
307 OccupiedEntry<'a, T, S, A>
308{
309 pub(super) fn new(
310 map: &'a mut BiHashMap<T, S, A>,
311 indexes: EntryIndexes,
312 ) -> Self {
313 OccupiedEntry { map, indexes }
314 }
315
316 /// Returns true if the entry is unique.
317 ///
318 /// Since [`BiHashMap`] is keyed by two keys, it's possible for
319 /// `OccupiedEntry` to match up to two separate items. This function returns
320 /// true if the entry is unique, meaning all keys point to exactly one item.
321 pub fn is_unique(&self) -> bool {
322 self.indexes.is_unique()
323 }
324
325 /// Returns true if the `OccupiedEntry` represents more than one item, or if
326 /// some keys are not present.
327 #[inline]
328 pub fn is_non_unique(&self) -> bool {
329 !self.is_unique()
330 }
331
332 /// Returns references to values that match the provided keys.
333 ///
334 /// If you need a reference to `T` that may outlive the destruction of the
335 /// `Entry` value, see [`into_ref`](Self::into_ref).
336 pub fn get(&self) -> OccupiedEntryRef<'_, T> {
337 self.map.get_by_entry_index(self.indexes)
338 }
339
340 /// Returns mutable references to values that match the provided keys.
341 ///
342 /// If you need a reference to `T` that may outlive the destruction of the
343 /// `Entry` value, see [`into_mut`](Self::into_mut).
344 pub fn get_mut(&mut self) -> OccupiedEntryMut<'_, T, S> {
345 self.map.get_by_entry_index_mut(self.indexes)
346 }
347
348 /// Converts self into shared references to items that match the provided
349 /// keys.
350 ///
351 /// If you need multiple references to the `OccupiedEntry`, see
352 /// [`get`](Self::get).
353 pub fn into_ref(self) -> OccupiedEntryRef<'a, T> {
354 self.map.get_by_entry_index(self.indexes)
355 }
356
357 /// Converts self into mutable references to items that match the provided
358 /// keys.
359 ///
360 /// If you need multiple references to the `OccupiedEntry`, see
361 /// [`get_mut`](Self::get_mut).
362 pub fn into_mut(self) -> OccupiedEntryMut<'a, T, S> {
363 self.map.get_by_entry_index_mut(self.indexes)
364 }
365
366 /// Sets the entry to a new value, returning all values that conflict.
367 ///
368 /// # Panics
369 ///
370 /// Panics if the passed-in key is different from the key of the entry.
371 pub fn insert(&mut self, value: T) -> Vec<T> {
372 // Note that `replace_at_indexes` panics if the keys don't match.
373 let (index, old_items) =
374 self.map.replace_at_indexes(self.indexes, value);
375 self.indexes = EntryIndexes::Unique(index);
376 old_items
377 }
378
379 /// Takes ownership of the values from the map.
380 pub fn remove(self) -> Vec<T> {
381 self.map.remove_by_entry_index(self.indexes)
382 }
383}
384
385/// A view into an occupied entry in a [`BiHashMap`].
386///
387/// Returned by [`OccupiedEntry::get`].
388#[derive(Debug)]
389pub enum OccupiedEntryRef<'a, T: BiHashItem> {
390 /// All keys point to the same entry.
391 Unique(&'a T),
392
393 /// The keys point to different entries, or some keys are not present.
394 ///
395 /// At least one of `by_key1` and `by_key2` is `Some`.
396 NonUnique {
397 /// The value fetched by the first key.
398 by_key1: Option<&'a T>,
399
400 /// The value fetched by the second key.
401 by_key2: Option<&'a T>,
402 },
403}
404
405impl<'a, T: BiHashItem> OccupiedEntryRef<'a, T> {
406 /// Returns true if the entry is unique.
407 ///
408 /// Since [`BiHashMap`] is keyed by two keys, it's possible for
409 /// `OccupiedEntry` to match up to two separate items. This function returns
410 /// true if the entry is unique, meaning all keys point to exactly one item.
411 #[inline]
412 pub fn is_unique(&self) -> bool {
413 matches!(self, Self::Unique(_))
414 }
415
416 /// Returns true if the `OccupiedEntryRef` represents more than one item, or
417 /// if some keys are not present.
418 #[inline]
419 pub fn is_non_unique(&self) -> bool {
420 matches!(self, Self::NonUnique { .. })
421 }
422
423 /// Returns a reference to the value if it is unique.
424 #[inline]
425 pub fn as_unique(&self) -> Option<&'a T> {
426 match self {
427 Self::Unique(v) => Some(v),
428 Self::NonUnique { .. } => None,
429 }
430 }
431
432 /// Returns a reference to the value fetched by the first key.
433 #[inline]
434 pub fn by_key1(&self) -> Option<&'a T> {
435 match self {
436 Self::Unique(v) => Some(v),
437 Self::NonUnique { by_key1, .. } => *by_key1,
438 }
439 }
440
441 /// Returns a reference to the value fetched by the second key.
442 #[inline]
443 pub fn by_key2(&self) -> Option<&'a T> {
444 match self {
445 Self::Unique(v) => Some(v),
446 Self::NonUnique { by_key2, .. } => *by_key2,
447 }
448 }
449}
450
451/// A mutable view into an occupied entry in a [`BiHashMap`].
452///
453/// Returned by [`OccupiedEntry::get_mut`].
454pub enum OccupiedEntryMut<
455 'a,
456 T: BiHashItem,
457 S: Clone + BuildHasher = DefaultHashBuilder,
458> {
459 /// All keys point to the same entry.
460 Unique(RefMut<'a, T, S>),
461
462 /// The keys point to different entries, or some keys are not present.
463 NonUnique {
464 /// The value fetched by the first key.
465 by_key1: Option<RefMut<'a, T, S>>,
466
467 /// The value fetched by the second key.
468 by_key2: Option<RefMut<'a, T, S>>,
469 },
470}
471
472impl<'a, T: BiHashItem + fmt::Debug, S: Clone + BuildHasher> fmt::Debug
473 for OccupiedEntryMut<'a, T, S>
474{
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476 match self {
477 OccupiedEntryMut::Unique(ref_mut) => {
478 f.debug_tuple("Unique").field(ref_mut).finish()
479 }
480 OccupiedEntryMut::NonUnique { by_key1, by_key2 } => f
481 .debug_struct("NonUnique")
482 .field("by_key1", by_key1)
483 .field("by_key2", by_key2)
484 .finish(),
485 }
486 }
487}
488
489impl<'a, T: BiHashItem, S: Clone + BuildHasher> OccupiedEntryMut<'a, T, S> {
490 /// Returns true if the entry is unique.
491 #[inline]
492 pub fn is_unique(&self) -> bool {
493 matches!(self, Self::Unique(_))
494 }
495
496 /// Returns true if the `OccupiedEntryMut` represents more than one item, or
497 /// if some keys are not present.
498 #[inline]
499 pub fn is_non_unique(&self) -> bool {
500 matches!(self, Self::NonUnique { .. })
501 }
502
503 /// Returns a reference to the value if it is unique.
504 #[inline]
505 pub fn as_unique(&mut self) -> Option<RefMut<'_, T, S>> {
506 match self {
507 Self::Unique(v) => Some(v.reborrow()),
508 Self::NonUnique { .. } => None,
509 }
510 }
511
512 /// Returns a mutable reference to the value fetched by the first key.
513 #[inline]
514 pub fn by_key1(&mut self) -> Option<RefMut<'_, T, S>> {
515 match self {
516 Self::Unique(v) => Some(v.reborrow()),
517 Self::NonUnique { by_key1, .. } => {
518 by_key1.as_mut().map(|v| v.reborrow())
519 }
520 }
521 }
522
523 /// Returns a mutable reference to the value fetched by the second key.
524 #[inline]
525 pub fn by_key2(&mut self) -> Option<RefMut<'_, T, S>> {
526 match self {
527 Self::Unique(v) => Some(v.reborrow()),
528 Self::NonUnique { by_key2, .. } => {
529 by_key2.as_mut().map(|v| v.reborrow())
530 }
531 }
532 }
533
534 /// Calls a callback for each value.
535 pub fn for_each<F>(&mut self, mut f: F)
536 where
537 F: FnMut(RefMut<'_, T, S>),
538 {
539 match self {
540 Self::Unique(v) => f(v.reborrow()),
541 Self::NonUnique { by_key1, by_key2 } => {
542 if let Some(v) = by_key1 {
543 f(v.reborrow());
544 }
545 if let Some(v) = by_key2 {
546 f(v.reborrow());
547 }
548 }
549 }
550 }
551}