-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement O(1) slice::Iter{,Mut} methods. #24701
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't: there's the pointer comparisons below. I think this would be better as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. |
||
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::<T>(); | ||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like |
||
unsafe { | ||
::intrinsics::assume(!self.ptr.is_null()); | ||
::intrinsics::assume(!self.end.is_null()); | ||
if self.end == self.ptr { | ||
None | ||
} else { | ||
if mem::size_of::<T>() == 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")] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this does change the semantics of
count
from the default implementation which exhausts the iterator. Perhaps this could modify start to equal end so retain equivalent semantics?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er actually this applies to all of the functions below as well as in the semantics have changed with respect to the default implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm mistaken, this consumes the iterator so it shouldn't matter. Is that not the case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that
size_hint
doesn't consume the iterator, so I don't think this consumes the iterator?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No but the function itself (
count(self)
) does.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh dear that is a very good point! After rereading
nth
I see it's updating the pointers already, in which case you can just completely ignore me :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it changes the behavior if this was called on a
by_ref()
iterator. But these have odd behavior anyway.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but then this count won't be called (it can't because it needs to consume the iterator by value). Instead the default (slow) Iterator count will be called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole comment thread says to me that we need a specific
consume
method for just running through an iterator, because people abusecount
for this today.