From 3669c576972f2d5f721ef6fa34a117f9cde9b373 Mon Sep 17 00:00:00 2001 From: Clark Gaebel Date: Tue, 25 Nov 2014 17:01:03 -0800 Subject: [PATCH 1/3] Make hashmap iterators implement ExactSize --- src/libstd/collections/hash/map.rs | 29 +++++++++++- src/libstd/collections/hash/table.rs | 71 ++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 662ae913764f4..cc7bb8600b98d 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -20,7 +20,7 @@ use cmp::{max, Eq, Equiv, PartialEq}; use default::Default; use fmt::{mod, Show}; use hash::{Hash, Hasher, RandomSipHasher}; -use iter::{mod, Iterator, FromIterator, Extend}; +use iter::{mod, Iterator, DoubleEndedIterator, ExactSize, FromIterator, Extend}; use kinds::Sized; use mem::{mod, replace}; use num::UnsignedInt; @@ -1325,6 +1325,15 @@ impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> { } } +impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a V)> for Entries<'a, K, V> { + #[inline] + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + self.inner.next_back() + } +} + +impl<'a, K, V> ExactSize<(&'a K, &'a V)> for Entries<'a, K, V> {} + impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { #[inline] fn next(&mut self) -> Option<(&'a K, &'a mut V)> { @@ -1336,6 +1345,15 @@ impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { } } +impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { + #[inline] + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + self.inner.next_back() + } +} + +impl<'a, K, V> ExactSize<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {} + impl Iterator<(K, V)> for MoveEntries { #[inline] fn next(&mut self) -> Option<(K, V)> { @@ -1347,6 +1365,15 @@ impl Iterator<(K, V)> for MoveEntries { } } +impl DoubleEndedIterator<(K, V)> for MoveEntries { + #[inline] + fn next_back(&mut self) -> Option<(K, V)> { + self.inner.next_back() + } +} + +impl ExactSize<(K, V)> for MoveEntries {} + impl<'a, K, V> OccupiedEntry<'a, K, V> { /// Gets a reference to the value in the entry pub fn get(&self) -> &V { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index f41ccea0aaf03..39052e720895e 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -15,7 +15,7 @@ pub use self::BucketState::*; use clone::Clone; use cmp; use hash::{Hash, Hasher}; -use iter::{Iterator, count}; +use iter::{Iterator, DoubleEndedIterator, ExactSize, count}; use kinds::{Sized, marker}; use mem::{min_align_of, size_of}; use mem; @@ -645,6 +645,7 @@ impl RawTable { fn raw_buckets(&self) -> RawBuckets { RawBuckets { raw: self.first_bucket_raw(), + hashes_start: self.hashes, hashes_end: unsafe { self.hashes.offset(self.capacity as int) }, @@ -667,11 +668,12 @@ impl RawTable { } pub fn into_iter(self) -> MoveEntries { - let RawBuckets { raw, hashes_end, .. } = self.raw_buckets(); + let RawBuckets { raw, hashes_start, hashes_end, .. } = self.raw_buckets(); // Replace the marker regardless of lifetime bounds on parameters. MoveEntries { iter: RawBuckets { raw: raw, + hashes_start: hashes_start, hashes_end: hashes_end, marker: marker::ContravariantLifetime, }, @@ -696,7 +698,8 @@ impl RawTable { /// this interface is safe, it's not used outside this module. struct RawBuckets<'a, K, V> { raw: RawBucket, - hashes_end: *mut u64, + hashes_start: *mut u64, // points to the hash before the first hash. + hashes_end: *mut u64, marker: marker::ContravariantLifetime<'a>, } @@ -717,6 +720,21 @@ impl<'a, K, V> Iterator> for RawBuckets<'a, K, V> { } } +impl<'a, K, V> DoubleEndedIterator> for RawBuckets<'a, K, V> { + fn next_back(&mut self) -> Option> { + while self.raw.hash != self.hashes_start { + unsafe { + let next = ptr::replace(&mut self.raw, self.raw.offset(-1)); + if *next.hash != EMPTY_BUCKET { + return Some(next); + } + } + } + + None + } +} + /// An iterator that moves out buckets in reverse order. It leaves the table /// in an inconsistent state and should only be used for dropping /// the table's remaining entries. It's used in the implementation of Drop. @@ -785,6 +803,20 @@ impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> { } } +impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a V)> for Entries<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + self.iter.next_back().map(|bucket| { + self.elems_left += 1; + unsafe { + (&*bucket.key, + &*bucket.val) + } + }) + } +} + +impl<'a, K, V> ExactSize<(&'a K, &'a V)> for Entries<'a, K, V> {} + impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.iter.next().map(|bucket| { @@ -801,6 +833,20 @@ impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { } } +impl<'a, K, V> DoubleEndedIterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + self.iter.next().map(|bucket| { + self.elems_left += 1; + unsafe { + (&*bucket.key, + &mut *bucket.val) + } + }) + } +} + +impl<'a, K, V> ExactSize<(&'a K, &'a mut V)> for MutEntries<'a, K, V> {} + impl Iterator<(SafeHash, K, V)> for MoveEntries { fn next(&mut self) -> Option<(SafeHash, K, V)> { self.iter.next().map(|bucket| { @@ -823,6 +869,25 @@ impl Iterator<(SafeHash, K, V)> for MoveEntries { } } +impl DoubleEndedIterator<(SafeHash, K, V)> for MoveEntries { + fn next_back(&mut self) -> Option<(SafeHash, K, V)> { + self.iter.next().map(|bucket| { + self.table.size += 1; + unsafe { + ( + SafeHash { + hash: *bucket.hash, + }, + ptr::read(bucket.key as *const K), + ptr::read(bucket.val as *const V) + ) + } + }) + } +} + +impl ExactSize<(SafeHash, K, V)> for MoveEntries {} + impl Clone for RawTable { fn clone(&self) -> RawTable { unsafe { From 60c29c4c988b792153c3961ecdd1694b6b5bbbb9 Mon Sep 17 00:00:00 2001 From: Clark Gaebel Date: Tue, 25 Nov 2014 17:15:46 -0800 Subject: [PATCH 2/3] fixed reverse iteration logic --- src/libstd/collections/hash/table.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 39052e720895e..783ddeaf1e4d0 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -697,19 +697,18 @@ impl RawTable { /// A raw iterator. The basis for some other iterators in this module. Although /// this interface is safe, it's not used outside this module. struct RawBuckets<'a, K, V> { - raw: RawBucket, - hashes_start: *mut u64, // points to the hash before the first hash. - hashes_end: *mut u64, + start: RawBucket, + end: RawBucket, // points one after the end. marker: marker::ContravariantLifetime<'a>, } impl<'a, K, V> Iterator> for RawBuckets<'a, K, V> { fn next(&mut self) -> Option> { - while self.raw.hash != self.hashes_end { + while self.raw.hash != self.end.hash { unsafe { // We are swapping out the pointer to a bucket and replacing // it with the pointer to the next one. - let prev = ptr::replace(&mut self.raw, self.raw.offset(1)); + let prev = ptr::replace(&mut self.start, self.start.offset(1)); if *prev.hash != EMPTY_BUCKET { return Some(prev); } @@ -722,9 +721,9 @@ impl<'a, K, V> Iterator> for RawBuckets<'a, K, V> { impl<'a, K, V> DoubleEndedIterator> for RawBuckets<'a, K, V> { fn next_back(&mut self) -> Option> { - while self.raw.hash != self.hashes_start { + while self.start.hash != self.end.hash { unsafe { - let next = ptr::replace(&mut self.raw, self.raw.offset(-1)); + let next = ptr::replace(&mut self.end, self.end.offset(-1)); if *next.hash != EMPTY_BUCKET { return Some(next); } @@ -739,7 +738,7 @@ impl<'a, K, V> DoubleEndedIterator> for RawBuckets<'a, K, V> { /// in an inconsistent state and should only be used for dropping /// the table's remaining entries. It's used in the implementation of Drop. struct RevMoveBuckets<'a, K, V> { - raw: RawBucket, + start: RawBucket, hashes_end: *mut u64, elems_left: uint, marker: marker::ContravariantLifetime<'a>, From 5cfaeddd006143aa645315cff15e20097752ef72 Mon Sep 17 00:00:00 2001 From: Clark Gaebel Date: Tue, 25 Nov 2014 17:23:16 -0800 Subject: [PATCH 3/3] fix the build --- src/libstd/collections/hash/table.rs | 37 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 783ddeaf1e4d0..919d5b63245e2 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -620,6 +620,25 @@ impl RawTable { } } + fn one_past_last_bucket_raw(&self) -> RawBucket { + let hashes_size = self.capacity * size_of::(); + let keys_size = self.capacity * size_of::(); + let vals_size = self.capacity * size_of::(); + + let buffer = self.hashes as *mut u8; + let (keys_offset, vals_offset) = calculate_offsets(hashes_size, + keys_size, min_align_of::(), + min_align_of::()); + + unsafe { + RawBucket { + hash: self.hashes.offset(hashes_size as int), + key: buffer.offset(keys_offset as int + keys_size as int) as *mut K, + val: buffer.offset(vals_offset as int + vals_size as int) as *mut V + } + } + } + /// Creates a new raw table from a given capacity. All buckets are /// initially empty. #[allow(experimental)] @@ -644,11 +663,8 @@ impl RawTable { fn raw_buckets(&self) -> RawBuckets { RawBuckets { - raw: self.first_bucket_raw(), - hashes_start: self.hashes, - hashes_end: unsafe { - self.hashes.offset(self.capacity as int) - }, + start: self.first_bucket_raw(), + end: self.one_past_last_bucket_raw(), marker: marker::ContravariantLifetime, } } @@ -668,13 +684,12 @@ impl RawTable { } pub fn into_iter(self) -> MoveEntries { - let RawBuckets { raw, hashes_start, hashes_end, .. } = self.raw_buckets(); + let RawBuckets { start, end, .. } = self.raw_buckets(); // Replace the marker regardless of lifetime bounds on parameters. MoveEntries { iter: RawBuckets { - raw: raw, - hashes_start: hashes_start, - hashes_end: hashes_end, + start: start, + end: end, marker: marker::ContravariantLifetime, }, table: self, @@ -704,7 +719,7 @@ struct RawBuckets<'a, K, V> { impl<'a, K, V> Iterator> for RawBuckets<'a, K, V> { fn next(&mut self) -> Option> { - while self.raw.hash != self.end.hash { + while self.start.hash != self.end.hash { unsafe { // We are swapping out the pointer to a bucket and replacing // it with the pointer to the next one. @@ -738,7 +753,7 @@ impl<'a, K, V> DoubleEndedIterator> for RawBuckets<'a, K, V> { /// in an inconsistent state and should only be used for dropping /// the table's remaining entries. It's used in the implementation of Drop. struct RevMoveBuckets<'a, K, V> { - start: RawBucket, + raw: RawBucket, hashes_end: *mut u64, elems_left: uint, marker: marker::ContravariantLifetime<'a>,