From 81b570ef72ec8ebf8ac8cd1027a8f4161a35484e Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:04:52 +0200 Subject: [PATCH 1/8] Implement IntoIter on SmallVec This contains a forced refactoring of the current `into_iter` method which was renamed to `drain` as it would otherwise conflict with `impl IntoIter for SmallVec { ... }` --- lib.rs | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 17 deletions(-) diff --git a/lib.rs b/lib.rs index e4e3765..8bd0fc3 100644 --- a/lib.rs +++ b/lib.rs @@ -51,11 +51,11 @@ unsafe fn deallocate(ptr: *mut T, capacity: usize) { // Let it drop. } -pub struct SmallVecMoveIterator<'a, T: 'a> { +pub struct Drain<'a, T: 'a> { iter: slice::IterMut<'a,T>, } -impl<'a, T: 'a> Iterator for SmallVecMoveIterator<'a,T> { +impl<'a, T: 'a> Iterator for Drain<'a,T> { type Item = T; #[inline] @@ -71,7 +71,7 @@ impl<'a, T: 'a> Iterator for SmallVecMoveIterator<'a,T> { } } -impl<'a, T: 'a> Drop for SmallVecMoveIterator<'a,T> { +impl<'a, T: 'a> Drop for Drain<'a,T> { fn drop(&mut self) { // Destroy the remaining elements. for _ in self.by_ref() {} @@ -83,6 +83,15 @@ enum SmallVecData { Heap { ptr: *mut A::Item, capacity: usize }, } +impl SmallVecData { + fn ptr_mut(&mut self) -> *mut A::Item { + match *self { + Inline { ref mut array } => array.ptr_mut(), + Heap { ptr, .. } => ptr, + } + } +} + unsafe impl Send for SmallVecData {} unsafe impl Sync for SmallVecData {} @@ -137,22 +146,16 @@ impl SmallVec { } } - /// NB: For efficiency reasons (avoiding making a second copy of the inline elements), this - /// actually clears out the original array instead of moving it. - /// FIXME: Rename this to `drain`? It’s more like `Vec::drain` than `Vec::into_iter`. - pub fn into_iter<'a>(&'a mut self) -> SmallVecMoveIterator<'a, A::Item> { + pub fn drain(&mut self) -> Drain { unsafe { let current_len = self.len(); self.set_len(0); - let ptr = match self.data { - Inline { ref mut array } => array.ptr_mut(), - Heap { ptr, .. } => ptr, - }; + let ptr = self.data.ptr_mut(); let slice = slice::from_raw_parts_mut(ptr, current_len); - SmallVecMoveIterator { + Drain { iter: slice.iter_mut(), } } @@ -305,10 +308,7 @@ impl ops::Deref for SmallVec { impl ops::DerefMut for SmallVec { #[inline] fn deref_mut(&mut self) -> &mut [A::Item] { - let ptr = match self.data { - Inline { ref mut array } => array.ptr_mut(), - Heap { ptr, .. } => ptr, - }; + let ptr = self.data.ptr_mut(); unsafe { slice::from_raw_parts_mut(ptr, self.len) } @@ -445,6 +445,70 @@ impl Ord for SmallVec where A::Item: Ord { unsafe impl Send for SmallVec where A::Item: Send {} +pub struct IntoIter { + data: SmallVecData, + current: usize, + end: usize, +} + +impl Drop for IntoIter { + fn drop(&mut self) { + for _ in self { } + } +} + +impl Iterator for IntoIter { + type Item = A::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.current == self.end { + None + } + else { + unsafe { + let current = self.current as isize; + self.current += 1; + Some(ptr::read(self.data.ptr_mut().offset(current))) + } + } + } +} + +impl IntoIterator for SmallVec { + type IntoIter = IntoIter; + type Item = A::Item; + fn into_iter(mut self) -> Self::IntoIter { + let len = self.len(); + unsafe { + // Only grab the `data` field, the `IntoIter` type handles dropping of the elements + let data = ptr::read(&mut self.data); + mem::forget(self); + IntoIter { + data: data, + current: 0, + end: len, + } + } + } +} + +impl<'a, A: Array> IntoIterator for &'a SmallVec { + type IntoIter = slice::Iter<'a, A::Item>; + type Item = &'a A::Item; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, A: Array> IntoIterator for &'a mut SmallVec { + type IntoIter = slice::IterMut<'a, A::Item>; + type Item = &'a mut A::Item; + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + // TODO: Remove these and its users. /// Deprecated alias to ease transition from an earlier version. @@ -497,6 +561,7 @@ impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, pub mod tests { use SmallVec; use std::borrow::ToOwned; + use std::cell::Cell; // We heap allocate all these strings so that double frees will show up under valgrind. @@ -563,6 +628,19 @@ pub mod tests { assert!(Some(SmallVec::<[&u32; 2]>::new()).is_some()); } + #[test] + fn drain() { + let mut v: SmallVec<[u8; 2]> = SmallVec::new(); + v.push(3); + assert_eq!(v.drain().collect::>(), &[3]); + + // spilling the vec + v.push(3); + v.push(4); + v.push(5); + assert_eq!(v.drain().collect::>(), &[3, 4, 5]); + } + #[test] fn into_iter() { let mut v: SmallVec<[u8; 2]> = SmallVec::new(); @@ -570,10 +648,49 @@ pub mod tests { assert_eq!(v.into_iter().collect::>(), &[3]); // spilling the vec + let mut v: SmallVec<[u8; 2]> = SmallVec::new(); v.push(3); v.push(4); v.push(5); - assert_eq!(v.into_iter().collect::>(), &[3, 4, 5]); + assert_eq!(v.drain().collect::>(), &[3, 4, 5]); + } + + #[test] + fn into_iter_drop() { + struct DropCounter<'a>(&'a Cell); + + impl<'a> Drop for DropCounter<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + + { + let cell = Cell::new(0); + let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); + v.push(DropCounter(&cell)); + v.into_iter(); + assert_eq!(cell.get(), 1); + } + + { + let cell = Cell::new(0); + let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); + v.push(DropCounter(&cell)); + v.push(DropCounter(&cell)); + assert!(v.into_iter().next().is_some()); + assert_eq!(cell.get(), 2); + } + + { + let cell = Cell::new(0); + let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); + v.push(DropCounter(&cell)); + v.push(DropCounter(&cell)); + v.push(DropCounter(&cell)); + assert!(v.into_iter().next().is_some()); + assert_eq!(cell.get(), 3); + } } #[test] From f53916cf7eced71c35ab1578745c4a9c15958656 Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:05:25 +0200 Subject: [PATCH 2/8] Implement Extend on SmallVec --- lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib.rs b/lib.rs index 8bd0fc3..1525ace 100644 --- a/lib.rs +++ b/lib.rs @@ -361,8 +361,8 @@ impl FromIterator for SmallVec { } } -impl SmallVec { - pub fn extend>(&mut self, iterable: I) { +impl Extend for SmallVec { + fn extend>(&mut self, iterable: I) { let iter = iterable.into_iter(); let (lower_size_bound, _) = iter.size_hint(); From 8e059c83b917f741016ce0fb43413ed7ae778e28 Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:17:50 +0200 Subject: [PATCH 3/8] Add AsRef, AsMut, Borrow, BorrowMut and Hash implementations --- lib.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 1525ace..a48c6c2 100644 --- a/lib.rs +++ b/lib.rs @@ -5,8 +5,10 @@ //! Small vectors in various sizes. These store a certain number of elements inline and fall back //! to the heap for larger allocations. +use std::borrow::{Borrow, BorrowMut}; use std::cmp; use std::fmt; +use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator}; use std::mem; use std::ops; @@ -315,6 +317,34 @@ impl ops::DerefMut for SmallVec { } } +impl AsRef<[A::Item]> for SmallVec { + #[inline] + fn as_ref(&self) -> &[A::Item] { + self + } +} + +impl AsMut<[A::Item]> for SmallVec { + #[inline] + fn as_mut(&mut self) -> &mut [A::Item] { + self + } +} + +impl Borrow<[A::Item]> for SmallVec { + #[inline] + fn borrow(&self) -> &[A::Item] { + self + } +} + +impl BorrowMut<[A::Item]> for SmallVec { + #[inline] + fn borrow_mut(&mut self) -> &mut [A::Item] { + self + } +} + macro_rules! impl_index { ($index_type: ty, $output_type: ty) => { impl ops::Index<$index_type> for SmallVec { @@ -443,6 +473,12 @@ impl Ord for SmallVec where A::Item: Ord { } } +impl Hash for SmallVec where A::Item: Hash { + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + unsafe impl Send for SmallVec where A::Item: Send {} pub struct IntoIter { @@ -561,7 +597,6 @@ impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, pub mod tests { use SmallVec; use std::borrow::ToOwned; - use std::cell::Cell; // We heap allocate all these strings so that double frees will show up under valgrind. @@ -657,6 +692,8 @@ pub mod tests { #[test] fn into_iter_drop() { + use std::cell::Cell; + struct DropCounter<'a>(&'a Cell); impl<'a> Drop for DropCounter<'a> { @@ -786,4 +823,24 @@ pub mod tests { assert!(b < c); assert!(c > b); } + + #[test] + fn test_hash() { + use std::hash::{Hash, SipHasher}; + + { + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + let b = [1, 2]; + a.extend(b.iter().cloned()); + let mut hasher = SipHasher::new(); + assert_eq!(a.hash(&mut hasher), b.hash(&mut hasher)); + } + { + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + let b = [1, 2, 11, 12]; + a.extend(b.iter().cloned()); + let mut hasher = SipHasher::new(); + assert_eq!(a.hash(&mut hasher), b.hash(&mut hasher)); + } + } } From d97f391cf392a3d31faea86417874ec04ae99c32 Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:30:38 +0200 Subject: [PATCH 4/8] Implement DoubleEndedIterator on IntoIter and Drain --- lib.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index a48c6c2..738fe6a 100644 --- a/lib.rs +++ b/lib.rs @@ -73,6 +73,20 @@ impl<'a, T: 'a> Iterator for Drain<'a,T> { } } +impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option { + match self.iter.next_back() { + None => None, + Some(reference) => { + unsafe { + Some(ptr::read(reference)) + } + } + } + } +} + impl<'a, T: 'a> Drop for Drain<'a,T> { fn drop(&mut self) { // Destroy the remaining elements. @@ -496,6 +510,7 @@ impl Drop for IntoIter { impl Iterator for IntoIter { type Item = A::Item; + #[inline] #[inline] fn next(&mut self) -> Option { if self.current == self.end { @@ -511,6 +526,21 @@ impl Iterator for IntoIter { } } +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + if self.current == self.end { + None + } + else { + unsafe { + self.end -= 1; + Some(ptr::read(self.data.ptr_mut().offset(self.end as isize))) + } + } + } +} + impl IntoIterator for SmallVec { type IntoIter = IntoIter; type Item = A::Item; @@ -676,6 +706,19 @@ pub mod tests { assert_eq!(v.drain().collect::>(), &[3, 4, 5]); } + #[test] + fn drain_rev() { + let mut v: SmallVec<[u8; 2]> = SmallVec::new(); + v.push(3); + assert_eq!(v.drain().rev().collect::>(), &[3]); + + // spilling the vec + v.push(3); + v.push(4); + v.push(5); + assert_eq!(v.drain().rev().collect::>(), &[5, 4, 3]); + } + #[test] fn into_iter() { let mut v: SmallVec<[u8; 2]> = SmallVec::new(); @@ -687,7 +730,21 @@ pub mod tests { v.push(3); v.push(4); v.push(5); - assert_eq!(v.drain().collect::>(), &[3, 4, 5]); + assert_eq!(v.into_iter().collect::>(), &[3, 4, 5]); + } + + #[test] + fn into_iter_rev() { + let mut v: SmallVec<[u8; 2]> = SmallVec::new(); + v.push(3); + assert_eq!(v.into_iter().rev().collect::>(), &[3]); + + // spilling the vec + let mut v: SmallVec<[u8; 2]> = SmallVec::new(); + v.push(3); + v.push(4); + v.push(5); + assert_eq!(v.into_iter().rev().collect::>(), &[5, 4, 3]); } #[test] @@ -728,6 +785,19 @@ pub mod tests { assert!(v.into_iter().next().is_some()); assert_eq!(cell.get(), 3); } + { + let cell = Cell::new(0); + let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); + v.push(DropCounter(&cell)); + v.push(DropCounter(&cell)); + v.push(DropCounter(&cell)); + { + let mut it = v.into_iter(); + assert!(it.next().is_some()); + assert!(it.next_back().is_some()); + } + assert_eq!(cell.get(), 3); + } } #[test] From 2bc365eab33af25d17fd1440f3086605ea7aa1be Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:35:53 +0200 Subject: [PATCH 5/8] Implement ExactSizeIterator on Drain and IntoIter --- lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib.rs b/lib.rs index 738fe6a..20209ff 100644 --- a/lib.rs +++ b/lib.rs @@ -71,6 +71,11 @@ impl<'a, T: 'a> Iterator for Drain<'a,T> { } } } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } } impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { @@ -87,6 +92,8 @@ impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { } } +impl<'a, T> ExactSizeIterator for Drain<'a, T> { } + impl<'a, T: 'a> Drop for Drain<'a,T> { fn drop(&mut self) { // Destroy the remaining elements. @@ -524,6 +531,12 @@ impl Iterator for IntoIter { } } } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let size = self.end - self.current; + (size, Some(size)) + } } impl DoubleEndedIterator for IntoIter { @@ -541,6 +554,8 @@ impl DoubleEndedIterator for IntoIter { } } +impl ExactSizeIterator for IntoIter { } + impl IntoIterator for SmallVec { type IntoIter = IntoIter; type Item = A::Item; From 727b2974de6b71d579dbcb8faa90f06eb4ed58fa Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 31 Jul 2016 21:52:51 +0200 Subject: [PATCH 6/8] Implement From<&[A::Item]> for SmallVec --- lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib.rs b/lib.rs index 20209ff..b828d22 100644 --- a/lib.rs +++ b/lib.rs @@ -366,6 +366,13 @@ impl BorrowMut<[A::Item]> for SmallVec { } } +impl<'a, A: Array> From<&'a [A::Item]> for SmallVec where A::Item: Clone { + #[inline] + fn from(slice: &'a [A::Item]) -> SmallVec { + slice.into_iter().cloned().collect() + } +} + macro_rules! impl_index { ($index_type: ty, $output_type: ty) => { impl ops::Index<$index_type> for SmallVec { From 724c1e9dad37e2a1642703acc050e7f14716df14 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 1 Aug 2016 15:50:41 +0200 Subject: [PATCH 7/8] Add tests for AsRef, AsMut, Borrow, BorrowMut, From and ExactSizeIterator --- lib.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lib.rs b/lib.rs index b828d22..fb40fc9 100644 --- a/lib.rs +++ b/lib.rs @@ -935,4 +935,69 @@ pub mod tests { assert_eq!(a.hash(&mut hasher), b.hash(&mut hasher)); } } + + #[test] + fn test_as_ref() { + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + a.push(1); + assert_eq!(a.as_ref(), [1]); + a.push(2); + assert_eq!(a.as_ref(), [1, 2]); + a.push(3); + assert_eq!(a.as_ref(), [1, 2, 3]); + } + + #[test] + fn test_as_mut() { + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + a.push(1); + assert_eq!(a.as_mut(), [1]); + a.push(2); + assert_eq!(a.as_mut(), [1, 2]); + a.push(3); + assert_eq!(a.as_mut(), [1, 2, 3]); + a.as_mut()[1] = 4; + assert_eq!(a.as_mut(), [1, 4, 3]); + } + + #[test] + fn test_borrow() { + use std::borrow::Borrow; + + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + a.push(1); + assert_eq!(a.borrow(), [1]); + a.push(2); + assert_eq!(a.borrow(), [1, 2]); + a.push(3); + assert_eq!(a.borrow(), [1, 2, 3]); + } + + #[test] + fn test_borrow_mut() { + use std::borrow::BorrowMut; + + let mut a: SmallVec<[u32; 2]> = SmallVec::new(); + a.push(1); + assert_eq!(a.borrow_mut(), [1]); + a.push(2); + assert_eq!(a.borrow_mut(), [1, 2]); + a.push(3); + assert_eq!(a.borrow_mut(), [1, 2, 3]); + BorrowMut::<[u32]>::borrow_mut(&mut a)[1] = 4; + assert_eq!(a.borrow_mut(), [1, 4, 3]); + } + + #[test] + fn test_from() { + assert_eq!(&SmallVec::<[u32; 2]>::from(&[1][..])[..], [1]); + assert_eq!(&SmallVec::<[u32; 2]>::from(&[1, 2, 3][..])[..], [1, 2, 3]); + } + + #[test] + fn test_exact_size_iterator() { + let mut vec = SmallVec::<[u32; 2]>::from(&[1, 2, 3][..]); + assert_eq!(vec.clone().into_iter().len(), 3); + assert_eq!(vec.drain().len(), 3); + } } From 200f4f2712ad5f0488d1f86cf4faa1686bba99b9 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 11 Aug 2016 08:31:56 +0200 Subject: [PATCH 8/8] Remove a duplicated #[inline] annotation --- lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib.rs b/lib.rs index fb40fc9..47b55d3 100644 --- a/lib.rs +++ b/lib.rs @@ -524,7 +524,6 @@ impl Drop for IntoIter { impl Iterator for IntoIter { type Item = A::Item; - #[inline] #[inline] fn next(&mut self) -> Option { if self.current == self.end {