Skip to main content

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}