From 15c41c87a1855813bea6b6f7e4ac81d66d0d2410 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Fri, 19 Aug 2016 22:13:39 -0700 Subject: [PATCH 1/2] Implement insert_slice for SmallVec Add benchmark for insert_many --- benches/bench.rs | 16 ++++++++++++ lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/benches/bench.rs b/benches/bench.rs index 8acb3c9..c571b85 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -38,6 +38,22 @@ fn bench_insert(b: &mut Bencher) { }); } +#[bench] +fn bench_insert_many(b: &mut Bencher) { + #[inline(never)] + fn insert_many_noinline>( + vec: &mut SmallVec<[u64; 16]>, index: usize, iterable: I) { + vec.insert_many(index, iterable) + } + + b.iter(|| { + let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); + insert_many_noinline(&mut vec, 0, 0..100); + insert_many_noinline(&mut vec, 0, 0..100); + vec + }); +} + #[bench] fn bench_extend(b: &mut Bencher) { b.iter(|| { diff --git a/lib.rs b/lib.rs index 09ecbba..5b320d5 100644 --- a/lib.rs +++ b/lib.rs @@ -313,6 +313,32 @@ impl SmallVec { self.set_len(len + 1); } } + + pub fn insert_many>(&mut self, index: usize, iterable: I) { + let iter = iterable.into_iter(); + let (lower_size_bound, _) = iter.size_hint(); + self.reserve(lower_size_bound); + + unsafe { + let ptr = self.as_mut_ptr().offset(index as isize); + let old_len = self.len; + ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index); + for (off, element) in iter.enumerate() { + if off < lower_size_bound { + ptr::write(ptr.offset(off as isize), element); + self.len = self.len + 1; + } else { + // Iterator provided more elements than the hint. + self.insert(index + off, element); + } + } + let num_added = self.len - old_len; + if num_added < lower_size_bound { + // Iterator provided less elements than the hint + ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index); + } + } + } } impl ops::Deref for SmallVec { @@ -867,6 +893,46 @@ pub mod tests { assert_eq!(&v.iter().map(|v| **v).collect::>(), &[0, 3, 2]); } + #[test] + fn test_insert_many() { + let mut v: SmallVec<[u8; 8]> = SmallVec::new(); + for x in 0..4 { + v.push(x); + } + assert_eq!(v.len(), 4); + v.insert_many(1, [5, 6].iter().cloned()); + assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); + } + + struct MockHintIter{x: T, hint: usize} + impl Iterator for MockHintIter { + type Item = T::Item; + fn next(&mut self) -> Option {self.x.next()} + fn size_hint(&self) -> (usize, Option) {(self.hint, None)} + } + + #[test] + fn test_insert_many_short_hint() { + let mut v: SmallVec<[u8; 8]> = SmallVec::new(); + for x in 0..4 { + v.push(x); + } + assert_eq!(v.len(), 4); + v.insert_many(1, MockHintIter{x: [5, 6].iter().cloned(), hint: 5}); + assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); + } + + #[test] + fn test_insert_many_long_hint() { + let mut v: SmallVec<[u8; 8]> = SmallVec::new(); + for x in 0..4 { + v.push(x); + } + assert_eq!(v.len(), 4); + v.insert_many(1, MockHintIter{x: [5, 6].iter().cloned(), hint: 1}); + assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); + } + #[test] #[should_panic] fn test_drop_panic_smallvec() { From 1c254ffaaa952a5c7e02e01a1a59a71597ec3a5d Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Mon, 3 Oct 2016 16:58:12 -0400 Subject: [PATCH 2/2] Assert to check for overflow --- lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib.rs b/lib.rs index 5b320d5..d0cd060 100644 --- a/lib.rs +++ b/lib.rs @@ -317,11 +317,14 @@ impl SmallVec { pub fn insert_many>(&mut self, index: usize, iterable: I) { let iter = iterable.into_iter(); let (lower_size_bound, _) = iter.size_hint(); + assert!(lower_size_bound <= std::isize::MAX as usize); // Ensure offset is indexable + assert!(index + lower_size_bound >= index); // Protect against overflow self.reserve(lower_size_bound); unsafe { - let ptr = self.as_mut_ptr().offset(index as isize); let old_len = self.len; + assert!(index <= old_len); + let ptr = self.as_mut_ptr().offset(index as isize); ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index); for (off, element) in iter.enumerate() { if off < lower_size_bound { @@ -329,12 +332,13 @@ impl SmallVec { self.len = self.len + 1; } else { // Iterator provided more elements than the hint. + assert!(index + off >= index); // Protect against overflow. self.insert(index + off, element); } } let num_added = self.len - old_len; if num_added < lower_size_bound { - // Iterator provided less elements than the hint + // Iterator provided fewer elements than the hint ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index); } }