From de8c79a53532c4a6de61b0dcde6ed9b133afa752 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 22 Apr 2015 16:03:56 -0400 Subject: [PATCH 1/3] Implement O(1) slice::Iter methods. Instead of using the O(n) defaults, define O(1) shortcuts. --- src/libcore/slice.rs | 54 ++++++++++++++++++++++++++++++++++++++++ src/libcoretest/slice.rs | 31 +++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 1e96d761d40..e2bb881921d 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -666,6 +666,60 @@ macro_rules! iterator { let exact = diff / (if size == 0 {1} else {size}); (exact, Some(exact)) } + + #[inline] + fn count(self) -> usize { + self.size_hint().0 + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<$elem> { + // could be implemented with slices, but this avoids bounds checks + unsafe { + ::intrinsics::assume(!self.ptr.is_null()); + ::intrinsics::assume(!self.end.is_null()); + // There should be some way to use offset and optimize this to LEA but I don't + // know how to do that AND detect overflow... + let size = mem::size_of::(); + if size == 0 { + if let Some(new_ptr) = (self.ptr as usize).checked_add(n) { + if new_ptr < (self.end as usize) { + self.ptr = transmute(new_ptr + 1); + return Some(&mut *(1 as *mut _)) + } + } + } else { + if let Some(new_ptr) = n.checked_mul(size).and_then(|offset| { + (self.ptr as usize).checked_add(offset) + }) { + if new_ptr < (self.end as usize) { + self.ptr = transmute(new_ptr + size); + return Some(transmute(new_ptr)) + } + } + } + None + } + } + + #[inline] + fn last(self) -> Option<$elem> { + // We could just call next_back but this avoids the memory write. + unsafe { + ::intrinsics::assume(!self.ptr.is_null()); + ::intrinsics::assume(!self.end.is_null()); + if self.end == self.ptr { + None + } else { + if mem::size_of::() == 0 { + // Use a non-null pointer value + Some(&mut *(1 as *mut _)) + } else { + Some(transmute(self.end.offset(-1))) + } + } + } + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcoretest/slice.rs b/src/libcoretest/slice.rs index fe73b3b4407..7c884a73ce0 100644 --- a/src/libcoretest/slice.rs +++ b/src/libcoretest/slice.rs @@ -82,3 +82,34 @@ fn iterator_to_slice() { test!([1u8,2,3]); test!([(),(),()]); } + +#[test] +fn test_iterator_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().nth(v.len()), None); + + let mut iter = v.iter(); + assert_eq!(iter.nth(2).unwrap(), &v[2]); + assert_eq!(iter.nth(1).unwrap(), &v[4]); +} + +#[test] +fn test_iterator_last() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v[..1].iter().last().unwrap(), &0); +} + +#[test] +fn test_iterator_count() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().count(), 5); + + let mut iter2 = v.iter(); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} From 50c6c6ca9038223226a36d9ceb15b6f37254959c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 22 Apr 2015 18:44:17 -0400 Subject: [PATCH 2/3] Address comments. 1. Use next_back for last. 2. Use slices for computing nth. It might be possible to use the same code for both the mut/const case but I don't know how that will play with compiler optimizations. --- src/libcore/slice.rs | 88 +++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index e2bb881921d..7bed0ef6daa 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -674,51 +674,13 @@ macro_rules! iterator { #[inline] fn nth(&mut self, n: usize) -> Option<$elem> { - // could be implemented with slices, but this avoids bounds checks - unsafe { - ::intrinsics::assume(!self.ptr.is_null()); - ::intrinsics::assume(!self.end.is_null()); - // There should be some way to use offset and optimize this to LEA but I don't - // know how to do that AND detect overflow... - let size = mem::size_of::(); - if size == 0 { - if let Some(new_ptr) = (self.ptr as usize).checked_add(n) { - if new_ptr < (self.end as usize) { - self.ptr = transmute(new_ptr + 1); - return Some(&mut *(1 as *mut _)) - } - } - } else { - if let Some(new_ptr) = n.checked_mul(size).and_then(|offset| { - (self.ptr as usize).checked_add(offset) - }) { - if new_ptr < (self.end as usize) { - self.ptr = transmute(new_ptr + size); - return Some(transmute(new_ptr)) - } - } - } - None - } + // Call helper method. Can't put the definition here because mut versus const. + self.iter_nth(n) } #[inline] - fn last(self) -> Option<$elem> { - // We could just call next_back but this avoids the memory write. - unsafe { - ::intrinsics::assume(!self.ptr.is_null()); - ::intrinsics::assume(!self.end.is_null()); - if self.end == self.ptr { - None - } else { - if mem::size_of::() == 0 { - // Use a non-null pointer value - Some(&mut *(1 as *mut _)) - } else { - Some(transmute(self.end.offset(-1))) - } - } - } + fn last(mut self) -> Option<$elem> { + self.next_back() } } @@ -839,6 +801,27 @@ impl<'a, T> Iter<'a, T> { pub fn as_slice(&self) -> &'a [T] { make_slice!(T => &'a [T]: self.ptr, self.end) } + + // Helper function for Iter::nth + fn iter_nth(&mut self, n: usize) -> Option<&'a T> { + match self.as_slice().get(n) { + Some(elem_ref) => if mem::size_of::() == 0 { + unsafe { + self.ptr = transmute((elem_ref as *const _) as usize + 1); + Some(& *(1 as *const _)) + } + } else { + unsafe { + self.ptr = (elem_ref as *const _).offset(1); + Some(elem_ref) + } + }, + None => { + self.ptr = self.end; + None + } + } + } } iterator!{struct Iter -> *const T, &'a T} @@ -968,6 +951,27 @@ impl<'a, T> IterMut<'a, T> { pub fn into_slice(self) -> &'a mut [T] { make_mut_slice!(T => &'a mut [T]: self.ptr, self.end) } + + // Helper function for IterMut::nth + fn iter_nth(&mut self, n: usize) -> Option<&'a mut T> { + match make_mut_slice!(T => &'a mut [T]: self.ptr, self.end).get_mut(n) { + Some(elem_ref) => if mem::size_of::() == 0 { + unsafe { + self.ptr = transmute((elem_ref as *mut _) as usize + 1); + Some(&mut *(1 as *mut _)) + } + } else { + unsafe { + self.ptr = (elem_ref as *mut _).offset(1); + Some(elem_ref) + } + }, + None => { + self.ptr = self.end; + None + } + } + } } iterator!{struct IterMut -> *mut T, &'a mut T} From e129b92c40678f65490ce58fa06079d38ae07eba Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 24 Apr 2015 12:55:19 -0400 Subject: [PATCH 3/3] Deduplicate slice iter offset/transmute code. --- src/libcore/slice.rs | 86 +++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 7bed0ef6daa..38e66c5a3d6 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -625,6 +625,36 @@ impl<'a, T> IntoIterator for &'a mut [T] { } } +#[inline(always)] +fn size_from_ptr(_: *const T) -> usize { + mem::size_of::() +} + + +// Use macro to be generic over const/mut +macro_rules! slice_offset { + ($ptr:expr, $by:expr) => {{ + let ptr = $ptr; + if size_from_ptr(ptr) == 0 { + transmute(ptr as usize + $by) + } else { + ptr.offset($by) + } + }}; +} + +macro_rules! slice_ref { + ($ptr:expr) => {{ + let ptr = $ptr; + if size_from_ptr(ptr) == 0 { + // Use a non-null pointer value + &mut *(1 as *mut _) + } else { + transmute(ptr) + } + }}; +} + // The shared definition of the `Iter` and `IterMut` iterators macro_rules! iterator { (struct $name:ident -> $ptr:ty, $elem:ty) => { @@ -641,20 +671,9 @@ macro_rules! iterator { if self.ptr == self.end { None } else { - if mem::size_of::() == 0 { - // purposefully don't use 'ptr.offset' because for - // vectors with 0-size elements this would return the - // same pointer. - self.ptr = transmute(self.ptr as usize + 1); - - // Use a non-null pointer value - Some(&mut *(1 as *mut _)) - } else { - let old = self.ptr; - self.ptr = self.ptr.offset(1); - - Some(transmute(old)) - } + let old = self.ptr; + self.ptr = slice_offset!(self.ptr, 1); + Some(slice_ref!(old)) } } } @@ -695,17 +714,8 @@ macro_rules! iterator { if self.end == self.ptr { None } else { - if mem::size_of::() == 0 { - // See above for why 'ptr.offset' isn't used - self.end = transmute(self.end as usize - 1); - - // Use a non-null pointer value - Some(&mut *(1 as *mut _)) - } else { - self.end = self.end.offset(-1); - - Some(transmute(self.end)) - } + self.end = slice_offset!(self.end, -1); + Some(slice_ref!(self.end)) } } } @@ -805,16 +815,9 @@ impl<'a, T> Iter<'a, T> { // Helper function for Iter::nth fn iter_nth(&mut self, n: usize) -> Option<&'a T> { match self.as_slice().get(n) { - Some(elem_ref) => if mem::size_of::() == 0 { - unsafe { - self.ptr = transmute((elem_ref as *const _) as usize + 1); - Some(& *(1 as *const _)) - } - } else { - unsafe { - self.ptr = (elem_ref as *const _).offset(1); - Some(elem_ref) - } + Some(elem_ref) => unsafe { + self.ptr = slice_offset!(elem_ref as *const _, 1); + Some(slice_ref!(elem_ref)) }, None => { self.ptr = self.end; @@ -955,16 +958,9 @@ impl<'a, T> IterMut<'a, T> { // Helper function for IterMut::nth fn iter_nth(&mut self, n: usize) -> Option<&'a mut T> { match make_mut_slice!(T => &'a mut [T]: self.ptr, self.end).get_mut(n) { - Some(elem_ref) => if mem::size_of::() == 0 { - unsafe { - self.ptr = transmute((elem_ref as *mut _) as usize + 1); - Some(&mut *(1 as *mut _)) - } - } else { - unsafe { - self.ptr = (elem_ref as *mut _).offset(1); - Some(elem_ref) - } + Some(elem_ref) => unsafe { + self.ptr = slice_offset!(elem_ref as *mut _, 1); + Some(slice_ref!(elem_ref)) }, None => { self.ptr = self.end;