diff options
| author | bors <bors@rust-lang.org> | 2014-12-21 07:22:45 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-12-21 07:22:45 +0000 |
| commit | ce468e643a9e048900e5495948737efdf5bb2385 (patch) | |
| tree | 8e07b9c8b42ba241bd6e9ecc51a1d35be064ad07 /src/libcollections | |
| parent | cc19e3380b4b7c63b6f1f79d1dfc213ea00e16cf (diff) | |
| parent | d57f25907bc4247b4d98efce5ab6948c35baa12d (diff) | |
| download | rust-ce468e643a9e048900e5495948737efdf5bb2385.tar.gz rust-ce468e643a9e048900e5495948737efdf5bb2385.zip | |
auto merge of #19946 : cgaebel/rust/hashmap-drain-iter, r=gankro
It is useful to move all the elements out of a hashmap without deallocating the underlying buffer. It came up in IRC, and this patch implements it as `drain`. r? @Gankro cc: @frankmcsherry
Diffstat (limited to 'src/libcollections')
| -rw-r--r-- | src/libcollections/binary_heap.rs | 41 | ||||
| -rw-r--r-- | src/libcollections/ring_buf.rs | 130 | ||||
| -rw-r--r-- | src/libcollections/vec.rs | 150 |
3 files changed, 313 insertions, 8 deletions
diff --git a/src/libcollections/binary_heap.rs b/src/libcollections/binary_heap.rs index e1c06736b36..a12bfcdbd18 100644 --- a/src/libcollections/binary_heap.rs +++ b/src/libcollections/binary_heap.rs @@ -551,9 +551,18 @@ impl<T: Ord> BinaryHeap<T> { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Clears the queue, returning an iterator over the removed elements. + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn drain<'a>(&'a mut self) -> Drain<'a, T> { + Drain { + iter: self.data.drain(), + } + } + /// Drops all items from the queue. #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn clear(&mut self) { self.data.truncate(0) } + pub fn clear(&mut self) { self.drain(); } } /// `BinaryHeap` iterator. @@ -596,6 +605,26 @@ impl<T> DoubleEndedIterator<T> for MoveItems<T> { impl<T> ExactSizeIterator<T> for MoveItems<T> {} +/// An iterator that drains a `BinaryHeap`. +pub struct Drain<'a, T: 'a> { + iter: vec::Drain<'a, T>, +} + +impl<'a, T: 'a> Iterator<T> for Drain<'a, T> { + #[inline] + fn next(&mut self) -> Option<T> { self.iter.next() } + + #[inline] + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} + +impl<'a, T: 'a> DoubleEndedIterator<T> for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<T> { self.iter.next_back() } +} + +impl<'a, T: 'a> ExactSizeIterator<T> for Drain<'a, T> {} + impl<T: Ord> FromIterator<T> for BinaryHeap<T> { fn from_iter<Iter: Iterator<T>>(iter: Iter) -> BinaryHeap<T> { let vec: Vec<T> = iter.collect(); @@ -819,4 +848,14 @@ mod tests { assert_eq!(q.pop().unwrap(), x); } } + + #[test] + fn test_drain() { + let mut q: BinaryHeap<_> = + [9u, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); + + assert_eq!(q.drain().take(5).count(), 5); + + assert!(q.is_empty()); + } } diff --git a/src/libcollections/ring_buf.rs b/src/libcollections/ring_buf.rs index c807ef611e2..59784f001a2 100644 --- a/src/libcollections/ring_buf.rs +++ b/src/libcollections/ring_buf.rs @@ -491,6 +491,27 @@ impl<T> RingBuf<T> { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Creates a draining iterator that clears the `RingBuf` and iterates over + /// the removed items from start to end. + /// + /// # Examples + /// + /// ``` + /// use std::collections::RingBuf; + /// + /// let mut v = RingBuf::new(); + /// v.push_back(1i); + /// assert_eq!(v.drain().next(), Some(1)); + /// assert!(v.is_empty()); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn drain<'a>(&'a mut self) -> Drain<'a, T> { + Drain { + inner: self, + } + } + /// Clears the buffer, removing all values. /// /// # Examples @@ -504,10 +525,9 @@ impl<T> RingBuf<T> { /// assert!(v.is_empty()); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] + #[inline] pub fn clear(&mut self) { - while self.pop_front().is_some() {} - self.head = 0; - self.tail = 0; + self.drain(); } /// Provides a reference to the front element, or `None` if the sequence is @@ -1230,9 +1250,44 @@ impl<T> DoubleEndedIterator<T> for MoveItems<T> { } } - impl<T> ExactSizeIterator<T> for MoveItems<T> {} +/// A draining RingBuf iterator +pub struct Drain<'a, T: 'a> { + inner: &'a mut RingBuf<T>, +} + +#[unsafe_destructor] +impl<'a, T: 'a> Drop for Drain<'a, T> { + fn drop(&mut self) { + for _ in *self {} + self.inner.head = 0; + self.inner.tail = 0; + } +} + +impl<'a, T: 'a> Iterator<T> for Drain<'a, T> { + #[inline] + fn next(&mut self) -> Option<T> { + self.inner.pop_front() + } + + #[inline] + fn size_hint(&self) -> (uint, Option<uint>) { + let len = self.inner.len(); + (len, Some(len)) + } +} + +impl<'a, T: 'a> DoubleEndedIterator<T> for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<T> { + self.inner.pop_back() + } +} + +impl<'a, T: 'a> ExactSizeIterator<T> for Drain<'a, T> {} + impl<A: PartialEq> PartialEq for RingBuf<A> { fn eq(&self, other: &RingBuf<A>) -> bool { self.len() == other.len() && @@ -1842,6 +1897,73 @@ mod tests { } #[test] + fn test_drain() { + + // Empty iter + { + let mut d: RingBuf<int> = RingBuf::new(); + + { + let mut iter = d.drain(); + + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert!(d.is_empty()); + } + + // simple iter + { + let mut d = RingBuf::new(); + for i in range(0i, 5) { + d.push_back(i); + } + + assert_eq!(d.drain().collect::<Vec<int>>(), [0, 1, 2, 3, 4]); + assert!(d.is_empty()); + } + + // wrapped iter + { + let mut d = RingBuf::new(); + for i in range(0i, 5) { + d.push_back(i); + } + for i in range(6, 9) { + d.push_front(i); + } + + assert_eq!(d.drain().collect::<Vec<int>>(), [8,7,6,0,1,2,3,4]); + assert!(d.is_empty()); + } + + // partially used + { + let mut d = RingBuf::new(); + for i in range(0i, 5) { + d.push_back(i); + } + for i in range(6, 9) { + d.push_front(i); + } + + { + let mut it = d.drain(); + assert_eq!(it.size_hint(), (8, Some(8))); + assert_eq!(it.next(), Some(8)); + assert_eq!(it.size_hint(), (7, Some(7))); + assert_eq!(it.next_back(), Some(4)); + assert_eq!(it.size_hint(), (6, Some(6))); + assert_eq!(it.next(), Some(7)); + assert_eq!(it.size_hint(), (5, Some(5))); + } + assert!(d.is_empty()); + } + } + + #[test] fn test_from_iter() { use core::iter; let v = vec!(1i,2,3,4,5,6,7); diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index e0745a86d71..faea37d1278 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1117,6 +1117,38 @@ impl<T> Vec<T> { } } + /// Creates a draining iterator that clears the `Vec` and iterates over + /// the removed items from start to end. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec!["a".to_string(), "b".to_string()]; + /// for s in v.drain() { + /// // s has type String, not &String + /// println!("{}", s); + /// } + /// assert!(v.is_empty()); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn drain<'a>(&'a mut self) -> Drain<'a, T> { + unsafe { + let begin = self.ptr as *const T; + let end = if mem::size_of::<T>() == 0 { + (self.ptr as uint + self.len()) as *const T + } else { + self.ptr.offset(self.len() as int) as *const T + }; + self.set_len(0); + Drain { + ptr: begin, + end: end, + marker: ContravariantLifetime, + } + } + } + /// Clears the vector, removing all values. /// /// # Examples @@ -1373,8 +1405,9 @@ pub struct MoveItems<T> { } impl<T> MoveItems<T> { - #[inline] /// Drops all items that have not yet been moved and returns the empty vector. + #[inline] + #[unstable] pub fn into_inner(mut self) -> Vec<T> { unsafe { for _x in self { } @@ -1384,8 +1417,8 @@ impl<T> MoveItems<T> { } } - /// Deprecated, use into_inner() instead - #[deprecated = "renamed to into_inner()"] + /// Deprecated, use .into_inner() instead + #[deprecated = "use .into_inner() instead"] pub fn unwrap(self) -> Vec<T> { self.into_inner() } } @@ -1461,6 +1494,84 @@ impl<T> Drop for MoveItems<T> { } } +/// An iterator that drains a vector. +#[unsafe_no_drop_flag] +pub struct Drain<'a, T> { + ptr: *const T, + end: *const T, + marker: ContravariantLifetime<'a>, +} + +impl<'a, T> Iterator<T> for Drain<'a, T> { + #[inline] + fn next(&mut self) -> Option<T> { + unsafe { + if self.ptr == self.end { + None + } else { + if mem::size_of::<T>() == 0 { + // purposefully don't use 'ptr.offset' because for + // vectors with 0-size elements this would return the + // same pointer. + self.ptr = mem::transmute(self.ptr as uint + 1); + + // Use a non-null pointer value + Some(ptr::read(mem::transmute(1u))) + } else { + let old = self.ptr; + self.ptr = self.ptr.offset(1); + + Some(ptr::read(old)) + } + } + } + } + + #[inline] + fn size_hint(&self) -> (uint, Option<uint>) { + let diff = (self.end as uint) - (self.ptr as uint); + let size = mem::size_of::<T>(); + let exact = diff / (if size == 0 {1} else {size}); + (exact, Some(exact)) + } +} + +impl<'a, T> DoubleEndedIterator<T> for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<T> { + unsafe { + if self.end == self.ptr { + None + } else { + if mem::size_of::<T>() == 0 { + // See above for why 'ptr.offset' isn't used + self.end = mem::transmute(self.end as uint - 1); + + // Use a non-null pointer value + Some(ptr::read(mem::transmute(1u))) + } else { + self.end = self.end.offset(-1); + + Some(ptr::read(self.end)) + } + } + } + } +} + +impl<'a, T> ExactSizeIterator<T> for Drain<'a, T> {} + +#[unsafe_destructor] +impl<'a, T> Drop for Drain<'a, T> { + fn drop(&mut self) { + // self.ptr == self.end == null if drop has already been called, + // so we can use #[unsafe_no_drop_flag]. + + // destroy the remaining elements + for _x in *self {} + } +} + /// Converts an iterator of pairs into a pair of vectors. /// /// Returns a tuple containing two vectors where the i-th element of the first vector contains the @@ -2268,6 +2379,39 @@ mod tests { } #[test] + fn test_drain_items() { + let mut vec = vec![1, 2, 3]; + let mut vec2: Vec<i32> = vec![]; + for i in vec.drain() { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [ 1, 2, 3 ]); + } + + #[test] + fn test_drain_items_reverse() { + let mut vec = vec![1, 2, 3]; + let mut vec2: Vec<i32> = vec![]; + for i in vec.drain().rev() { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [ 3, 2, 1 ]); + } + + #[test] + fn test_drain_items_zero_sized() { + let mut vec = vec![(), (), ()]; + let mut vec2: Vec<()> = vec![]; + for i in vec.drain() { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [(), (), ()]); + } + + #[test] fn test_into_boxed_slice() { let xs = vec![1u, 2, 3]; let ys = xs.into_boxed_slice(); |
