LCOV - code coverage report
Current view: top level - libs/neon-shmem/src/hash - entry.rs (source / functions) Coverage Total Hit
Test: 4be46b1c0003aa3bbac9ade362c676b419df4c20.info Lines: 72.2 % 54 39
Test Date: 2025-07-22 17:50:06 Functions: 66.7 % 6 4

            Line data    Source code
       1              : //! Equivalent of [`std::collections::hash_map::Entry`] for this hashmap.
       2              : 
       3              : use crate::hash::core::{CoreHashMap, FullError, INVALID_POS};
       4              : use crate::sync::{RwLockWriteGuard, ValueWriteGuard};
       5              : 
       6              : use std::hash::Hash;
       7              : use std::mem;
       8              : 
       9              : pub enum Entry<'a, 'b, K, V> {
      10              :     Occupied(OccupiedEntry<'a, 'b, K, V>),
      11              :     Vacant(VacantEntry<'a, 'b, K, V>),
      12              : }
      13              : 
      14              : /// Enum representing the previous position within a chain.
      15              : #[derive(Clone, Copy)]
      16              : pub(crate) enum PrevPos {
      17              :     /// Starting index within the dictionary.  
      18              :     First(u32),
      19              :     /// Regular index within the buckets.
      20              :     Chained(u32),
      21              :     /// Unknown - e.g. the associated entry was retrieved by index instead of chain.
      22              :     Unknown(u64),
      23              : }
      24              : 
      25              : pub struct OccupiedEntry<'a, 'b, K, V> {
      26              :     /// Mutable reference to the map containing this entry.
      27              :     pub(crate) map: RwLockWriteGuard<'b, CoreHashMap<'a, K, V>>,
      28              :     /// The key of the occupied entry
      29              :     pub(crate) _key: K,
      30              :     /// The index of the previous entry in the chain.
      31              :     pub(crate) prev_pos: PrevPos,
      32              :     /// The position of the bucket in the [`CoreHashMap`] bucket array.
      33              :     pub(crate) bucket_pos: u32,
      34              : }
      35              : 
      36              : impl<K, V> OccupiedEntry<'_, '_, K, V> {
      37            0 :     pub fn get(&self) -> &V {
      38            0 :         &self.map.buckets[self.bucket_pos as usize]
      39            0 :             .inner
      40            0 :             .as_ref()
      41            0 :             .unwrap()
      42            0 :             .1
      43            0 :     }
      44              : 
      45            0 :     pub fn get_mut(&mut self) -> &mut V {
      46            0 :         &mut self.map.buckets[self.bucket_pos as usize]
      47            0 :             .inner
      48            0 :             .as_mut()
      49            0 :             .unwrap()
      50            0 :             .1
      51            0 :     }
      52              : 
      53              :     /// Inserts a value into the entry, replacing (and returning) the existing value.
      54        64716 :     pub fn insert(&mut self, value: V) -> V {
      55        64716 :         let bucket = &mut self.map.buckets[self.bucket_pos as usize];
      56              :         // This assumes inner is Some, which it must be for an OccupiedEntry
      57        64716 :         mem::replace(&mut bucket.inner.as_mut().unwrap().1, value)
      58        64716 :     }
      59              : 
      60              :     /// Removes the entry from the hash map, returning the value originally stored within it.
      61              :     ///
      62              :     /// This may result in multiple bucket accesses if the entry was obtained by index as the
      63              :     /// previous chain entry needs to be discovered in this case.
      64        23501 :     pub fn remove(mut self) -> V {
      65              :         // If this bucket was queried by index, go ahead and follow its chain from the start.
      66        23501 :         let prev = if let PrevPos::Unknown(hash) = self.prev_pos {
      67          247 :             let dict_idx = hash as usize % self.map.dictionary.len();
      68          247 :             let mut prev = PrevPos::First(dict_idx as u32);
      69          247 :             let mut curr = self.map.dictionary[dict_idx];
      70          268 :             while curr != self.bucket_pos {
      71           21 :                 assert!(curr != INVALID_POS);
      72           21 :                 prev = PrevPos::Chained(curr);
      73           21 :                 curr = self.map.buckets[curr as usize].next;
      74              :             }
      75          247 :             prev
      76              :         } else {
      77        23254 :             self.prev_pos
      78              :         };
      79              : 
      80              :         // CoreHashMap::remove returns Option<(K, V)>. We know it's Some for an OccupiedEntry.
      81        23501 :         let bucket = &mut self.map.buckets[self.bucket_pos as usize];
      82              : 
      83              :         // unlink it from the chain
      84        23501 :         match prev {
      85        21586 :             PrevPos::First(dict_pos) => {
      86        21586 :                 self.map.dictionary[dict_pos as usize] = bucket.next;
      87        21586 :             }
      88         1915 :             PrevPos::Chained(bucket_pos) => {
      89         1915 :                 self.map.buckets[bucket_pos as usize].next = bucket.next;
      90         1915 :             }
      91            0 :             _ => unreachable!(),
      92              :         }
      93              : 
      94              :         // and add it to the freelist
      95        23501 :         let free = self.map.free_head;
      96        23501 :         let bucket = &mut self.map.buckets[self.bucket_pos as usize];
      97        23501 :         let old_value = bucket.inner.take();
      98        23501 :         bucket.next = free;
      99        23501 :         self.map.free_head = self.bucket_pos;
     100        23501 :         self.map.buckets_in_use -= 1;
     101              : 
     102        23501 :         old_value.unwrap().1
     103        23501 :     }
     104              : }
     105              : 
     106              : /// An abstract view into a vacant entry within the map.
     107              : pub struct VacantEntry<'a, 'b, K, V> {
     108              :     /// Mutable reference to the map containing this entry.
     109              :     pub(crate) map: RwLockWriteGuard<'b, CoreHashMap<'a, K, V>>,
     110              :     /// The key to be inserted into this entry.
     111              :     pub(crate) key: K,
     112              :     /// The position within the dictionary corresponding to the key's hash.
     113              :     pub(crate) dict_pos: u32,
     114              : }
     115              : 
     116              : impl<'b, K: Clone + Hash + Eq, V> VacantEntry<'_, 'b, K, V> {
     117              :     /// Insert a value into the vacant entry, finding and populating an empty bucket in the process.
     118              :     ///
     119              :     /// # Errors
     120              :     /// Will return [`FullError`] if there are no unoccupied buckets in the map.
     121       172328 :     pub fn insert(mut self, value: V) -> Result<ValueWriteGuard<'b, V>, FullError> {
     122       172328 :         let pos = self.map.alloc_bucket(self.key, value)?;
     123       172326 :         self.map.buckets[pos as usize].next = self.map.dictionary[self.dict_pos as usize];
     124       172326 :         self.map.dictionary[self.dict_pos as usize] = pos;
     125              : 
     126       172326 :         Ok(RwLockWriteGuard::map(self.map, |m| {
     127       172326 :             &mut m.buckets[pos as usize].inner.as_mut().unwrap().1
     128       172326 :         }))
     129       172328 :     }
     130              : }
        

Generated by: LCOV version 2.1-beta