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