diff options
| author | Piotr Czarnecki <pioczarn@gmail.com> | 2016-03-05 22:11:15 +0100 |
|---|---|---|
| committer | Piotr Czarnecki <pioczarn@gmail.com> | 2016-03-05 22:11:15 +0100 |
| commit | a619fdd2ad06c8aea1aa4f5043dfa243d5438b55 (patch) | |
| tree | 851e51ab9a0741cfbc199e120ff45182e327764b /src/libstd | |
| parent | c71f720d9bf331c2922eafc35c27ccac05e94e5d (diff) | |
| download | rust-a619fdd2ad06c8aea1aa4f5043dfa243d5438b55.tar.gz rust-a619fdd2ad06c8aea1aa4f5043dfa243d5438b55.zip | |
Add `InternalEntry` for use in all searches.
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/collections/hash/map.rs | 268 | ||||
| -rw-r--r-- | src/libstd/collections/hash/table.rs | 18 |
2 files changed, 119 insertions, 167 deletions
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 820b47af715..91626b8d16b 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -9,7 +9,6 @@ // except according to those terms. use self::Entry::*; -use self::SearchResult::*; use self::VacantEntryState::*; use borrow::Borrow; @@ -26,7 +25,6 @@ use super::table::{ Bucket, EmptyBucket, FullBucket, - FullBucketImm, FullBucketMut, RawTable, SafeHash @@ -342,10 +340,11 @@ pub struct HashMap<K, V, S = RandomState> { } /// Search for a pre-hashed key. +#[inline] fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) - -> SearchResult<K, V, M> where + -> InternalEntry<K, V, M> where M: Deref<Target=RawTable<K, V>>, F: FnMut(&K) -> bool, { @@ -353,37 +352,50 @@ fn search_hashed<K, V, M, F>(table: M, // undefined behavior when Bucket::new gets the raw bucket in this // case, immediately return the appropriate search result. if table.capacity() == 0 { - return TableRef(table); + return InternalEntry::TableIsEmpty; } - let size = table.size(); + let size = table.size() as isize; let mut probe = Bucket::new(table, hash); - let ib = probe.index(); + let ib = probe.index() as isize; - while probe.index() != ib + size { + loop { let full = match probe.peek() { - Empty(b) => return TableRef(b.into_table()), // hit an empty bucket - Full(b) => b + Empty(bucket) => { + // Found a hole! + return InternalEntry::Vacant { + hash: hash, + elem: NoElem(bucket), + }; + } + Full(bucket) => bucket }; - if full.distance() + ib < full.index() { + let robin_ib = full.index() as isize - full.displacement() as isize; + + if ib < robin_ib { + // Found a luckier bucket than me. // We can finish the search early if we hit any bucket // with a lower distance to initial bucket than we've probed. - return TableRef(full.into_table()); + return InternalEntry::Vacant { + hash: hash, + elem: NeqElem(full, robin_ib as usize), + }; } // If the hash doesn't match, it can't be this one.. if hash == full.hash() { // If the key doesn't match, it can't be this one.. if is_match(full.read().0) { - return FoundExisting(full); + return InternalEntry::Occupied { + elem: full + }; } } probe = full.next(); + assert!(probe.index() as isize != ib + size + 1); } - - TableRef(probe.into_table()) } fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { @@ -462,25 +474,6 @@ fn robin_hood<'a, K: 'a, V: 'a>(mut bucket: FullBucketMut<'a, K, V>, } } -/// A result that works like Option<FullBucket<..>> but preserves -/// the reference that grants us access to the table in any case. -enum SearchResult<K, V, M> { - // This is an entry that holds the given key: - FoundExisting(FullBucket<K, V, M>), - - // There was no such entry. The reference is given back: - TableRef(M) -} - -impl<K, V, M> SearchResult<K, V, M> { - fn into_option(self) -> Option<FullBucket<K, V, M>> { - match self { - FoundExisting(bucket) => Some(bucket), - TableRef(_) => None - } - } -} - impl<K, V, S> HashMap<K, V, S> where K: Eq + Hash, S: BuildHasher { @@ -491,20 +484,20 @@ impl<K, V, S> HashMap<K, V, S> /// Search for a key, yielding the index if it's found in the hashtable. /// If you already have the hash for the key lying around, use /// search_hashed. - fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> Option<FullBucketImm<'a, K, V>> + #[inline] + fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> InternalEntry<K, V, &'a RawTable<K, V>> where K: Borrow<Q>, Q: Eq + Hash { let hash = self.make_hash(q); search_hashed(&self.table, hash, |k| q.eq(k.borrow())) - .into_option() } - fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> Option<FullBucketMut<'a, K, V>> + #[inline] + fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> InternalEntry<K, V, &'a mut RawTable<K, V>> where K: Borrow<Q>, Q: Eq + Hash { let hash = self.make_hash(q); search_hashed(&mut self.table, hash, |k| q.eq(k.borrow())) - .into_option() } // The caller should ensure that invariants by Robin Hood Hashing hold. @@ -824,53 +817,19 @@ impl<K, V, S> HashMap<K, V, S> /// /// If the key already exists, the hashtable will be returned untouched /// and a reference to the existing element will be returned. - fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> &mut V { - self.insert_or_replace_with(hash, k, v, |_, _, _, _| ()) - } - - fn insert_or_replace_with<'a, F>(&'a mut self, - hash: SafeHash, - k: K, - v: V, - mut found_existing: F) - -> &'a mut V where - F: FnMut(&mut K, &mut V, K, V), - { - // Worst case, we'll find one empty bucket among `size + 1` buckets. - let size = self.table.size(); - let mut probe = Bucket::new(&mut self.table, hash); - let ib = probe.index(); - - loop { - let mut bucket = match probe.peek() { - Empty(bucket) => { - // Found a hole! - return bucket.put(hash, k, v).into_mut_refs().1; - } - Full(bucket) => bucket - }; - - // hash matches? - if bucket.hash() == hash { - // key matches? - if k == *bucket.read_mut().0 { - let (bucket_k, bucket_v) = bucket.into_mut_refs(); - debug_assert!(k == *bucket_k); - // Key already exists. Get its reference. - found_existing(bucket_k, bucket_v, k, v); - return bucket_v; - } + fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> Option<V> { + let entry = search_hashed(&mut self.table, hash, |key| *key == k).into_entry(k); + match entry { + Some(Occupied(mut elem)) => { + Some(elem.insert(v)) } - - let robin_ib = bucket.index() as isize - bucket.distance() as isize; - - if (ib as isize) < robin_ib { - // Found a luckier bucket than me. Better steal his spot. - return robin_hood(bucket, robin_ib as usize, hash, k, v); + Some(Vacant(elem)) => { + elem.insert(v); + None + } + None => { + unreachable!() } - - probe = bucket.next(); - assert!(probe.index() != ib + size + 1); } } @@ -997,9 +956,7 @@ impl<K, V, S> HashMap<K, V, S> pub fn entry(&mut self, key: K) -> Entry<K, V> { // Gotta resize now. self.reserve(1); - - let hash = self.make_hash(&key); - search_entry_hashed(&mut self.table, hash, key) + self.search_mut(&key).into_entry(key).expect("unreachable") } /// Returns the number of elements in the map. @@ -1102,7 +1059,7 @@ impl<K, V, S> HashMap<K, V, S> pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq { - self.search(k).map(|bucket| bucket.into_refs().1) + self.search(k).into_occupied_bucket().map(|bucket| bucket.into_refs().1) } /// Returns true if the map contains a value for the specified key. @@ -1125,7 +1082,7 @@ impl<K, V, S> HashMap<K, V, S> pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool where K: Borrow<Q>, Q: Hash + Eq { - self.search(k).is_some() + self.search(k).into_occupied_bucket().is_some() } /// Returns a mutable reference to the value corresponding to the key. @@ -1150,7 +1107,7 @@ impl<K, V, S> HashMap<K, V, S> pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> where K: Borrow<Q>, Q: Hash + Eq { - self.search_mut(k).map(|bucket| bucket.into_mut_refs().1) + self.search_mut(k).into_occupied_bucket().map(|bucket| bucket.into_mut_refs().1) } /// Inserts a key-value pair into the map. @@ -1181,12 +1138,7 @@ impl<K, V, S> HashMap<K, V, S> pub fn insert(&mut self, k: K, v: V) -> Option<V> { let hash = self.make_hash(&k); self.reserve(1); - - let mut retval = None; - self.insert_or_replace_with(hash, k, v, |_, val_ref, _, val| { - retval = Some(replace(val_ref, val)); - }); - retval + self.insert_hashed_nocheck(hash, k, v) } /// Removes a key from the map, returning the value at the key if the key @@ -1214,54 +1166,7 @@ impl<K, V, S> HashMap<K, V, S> return None } - self.search_mut(k).map(|bucket| pop_internal(bucket).1) - } -} - -fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHash, k: K) - -> Entry<'a, K, V> -{ - // Worst case, we'll find one empty bucket among `size + 1` buckets. - let size = table.size(); - let mut probe = Bucket::new(table, hash); - let ib = probe.index(); - - loop { - let bucket = match probe.peek() { - Empty(bucket) => { - // Found a hole! - return Vacant(VacantEntry { - hash: hash, - key: k, - elem: NoElem(bucket), - }); - }, - Full(bucket) => bucket - }; - - // hash matches? - if bucket.hash() == hash { - // key matches? - if k == *bucket.read().0 { - return Occupied(OccupiedEntry{ - elem: bucket, - }); - } - } - - let robin_ib = bucket.index() as isize - bucket.distance() as isize; - - if (ib as isize) < robin_ib { - // Found a luckier bucket than me. Better steal his spot. - return Vacant(VacantEntry { - hash: hash, - key: k, - elem: NeqElem(bucket, robin_ib as usize), - }); - } - - probe = bucket.next(); - assert!(probe.index() != ib + size + 1); + self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) } } @@ -1382,18 +1287,46 @@ pub struct Drain<'a, K: 'a, V: 'a> { inner: iter::Map<table::Drain<'a, K, V>, fn((SafeHash, K, V)) -> (K, V)> } -/// A view into a single occupied location in a HashMap. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - elem: FullBucket<K, V, &'a mut RawTable<K, V>>, +enum InternalEntry<K, V, M> { + Occupied { + elem: FullBucket<K, V, M>, + }, + Vacant { + hash: SafeHash, + elem: VacantEntryState<K, V, M>, + }, + TableIsEmpty, } -/// A view into a single empty location in a HashMap. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - hash: SafeHash, - key: K, - elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>, +impl<K, V, M> InternalEntry<K, V, M> { + #[inline] + fn into_occupied_bucket(self) -> Option<FullBucket<K, V, M>> { + match self { + InternalEntry::Occupied { elem } => Some(elem), + _ => None, + } + } +} + +impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { + #[inline] + fn into_entry(self, key: K) -> Option<Entry<'a, K, V>> { + match self { + InternalEntry::Occupied { elem } => { + Some(Occupied(OccupiedEntry { + elem: elem + })) + } + InternalEntry::Vacant { hash, elem } => { + Some(Vacant(VacantEntry { + hash: hash, + key: key, + elem: elem, + })) + } + InternalEntry::TableIsEmpty => None + } + } } /// A view into a single location in a map, which may be vacant or occupied. @@ -1412,6 +1345,20 @@ pub enum Entry<'a, K: 'a, V: 'a> { ), } +/// A view into a single occupied location in a HashMap. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + elem: FullBucket<K, V, &'a mut RawTable<K, V>>, +} + +/// A view into a single empty location in a HashMap. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VacantEntry<'a, K: 'a, V: 'a> { + hash: SafeHash, + key: K, + elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>, +} + /// Possible states of a VacantEntry. enum VacantEntryState<K, V, M> { /// The index is occupied, but the key to insert has precedence, @@ -1703,7 +1650,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> type Key = K; fn get(&self, key: &Q) -> Option<&K> { - self.search(key).map(|bucket| bucket.into_refs().0) + self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0) } fn take(&mut self, key: &Q) -> Option<K> { @@ -1711,18 +1658,21 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> return None } - self.search_mut(key).map(|bucket| pop_internal(bucket).0) + self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0) } fn replace(&mut self, key: K) -> Option<K> { let hash = self.make_hash(&key); self.reserve(1); - let mut retkey = None; - self.insert_or_replace_with(hash, key, (), |key_ref, _, key, _| { - retkey = Some(replace(key_ref, key)); - }); - retkey + match search_hashed(&mut self.table, hash, |k| *k == key) { + InternalEntry::Occupied { mut elem } => { + Some(mem::replace(elem.read_mut().0, key)) + } + _ => { + None + } + } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 3a7ff0c1e22..1a29c2c159a 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -180,6 +180,8 @@ impl<K, V> RawBucket<K, V> { } } +// Uncomment dead code when it's needed. + // Buckets hold references to the table. impl<K, V, M> FullBucket<K, V, M> { /// Borrow a reference to the table. @@ -201,17 +203,17 @@ impl<K, V, M> EmptyBucket<K, V, M> { pub fn table(&self) -> &M { &self.table } - /// Move out the reference to the table. - pub fn into_table(self) -> M { - self.table - } + // /// Move out the reference to the table. + // pub fn into_table(self) -> M { + // self.table + // } } impl<K, V, M> Bucket<K, V, M> { - /// Move out the reference to the table. - pub fn into_table(self) -> M { - self.table - } + // /// Move out the reference to the table. + // pub fn into_table(self) -> M { + // self.table + // } /// Get the raw index. pub fn index(&self) -> usize { self.idx |
