-
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 Vec::drain from RFC 574 #24781
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
#![unstable(feature = "collections_range", reason = "was just added")] | ||
|
||
//! Range syntax. | ||
|
||
use core::option::Option::{self, None, Some}; | ||
use core::ops::{RangeFull, Range, RangeTo, RangeFrom}; | ||
|
||
/// **RangeArgument** is implemented by Rust's built-in range types, produced | ||
/// by range syntax like `..`, `a..`, `..b` or `c..d`. | ||
pub trait RangeArgument<T> { | ||
/// Start index (inclusive) | ||
/// | ||
/// Return start value if present, else `None`. | ||
fn start(&self) -> Option<&T> { None } | ||
|
||
/// End index (exclusive) | ||
/// | ||
/// Return end value if present, else `None`. | ||
fn end(&self) -> Option<&T> { None } | ||
} | ||
|
||
|
||
impl<T> RangeArgument<T> for RangeFull {} | ||
|
||
impl<T> RangeArgument<T> for RangeFrom<T> { | ||
fn start(&self) -> Option<&T> { Some(&self.start) } | ||
} | ||
|
||
impl<T> RangeArgument<T> for RangeTo<T> { | ||
fn end(&self) -> Option<&T> { Some(&self.end) } | ||
} | ||
|
||
impl<T> RangeArgument<T> for Range<T> { | ||
fn start(&self) -> Option<&T> { Some(&self.start) } | ||
fn end(&self) -> Option<&T> { Some(&self.end) } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,8 @@ use core::usize; | |
|
||
use borrow::{Cow, IntoCow}; | ||
|
||
use super::range::RangeArgument; | ||
|
||
// FIXME- fix places which assume the max vector allowed has memory usize::MAX. | ||
static MAX_MEMORY_SIZE: usize = isize::MAX as usize; | ||
|
||
|
@@ -718,36 +720,61 @@ impl<T> Vec<T> { | |
unsafe { other.set_len(0); } | ||
} | ||
|
||
/// Creates a draining iterator that clears the `Vec` and iterates over | ||
/// the removed items from start to end. | ||
/// Create a draining iterator that removes the specified range in the vector | ||
/// and yields the removed items from start to end. The element range is | ||
/// removed even if the iterator is not consumed until the end. | ||
/// | ||
/// Note: It is unspecified how many elements are removed from the vector, | ||
/// if the `Drain` value is leaked. | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if the starting point is greater than the end point or if | ||
/// the end point is greater than the length of the vector. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// # #![feature(collections)] | ||
/// 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()); | ||
/// # #![feature(collections_drain, collections_range)] | ||
/// | ||
/// // Draining using `..` clears the whole vector. | ||
/// let mut v = vec![1, 2, 3]; | ||
/// let u: Vec<_> = v.drain(..).collect(); | ||
/// assert_eq!(v, &[]); | ||
/// assert_eq!(u, &[1, 2, 3]); | ||
/// ``` | ||
#[inline] | ||
#[unstable(feature = "collections", | ||
reason = "matches collection reform specification, waiting for dust to settle")] | ||
pub fn drain(&mut self) -> Drain<T> { | ||
#[unstable(feature = "collections_drain", | ||
reason = "recently added, matches RFC")] | ||
pub fn drain<R>(&mut self, range: R) -> Drain<T> where R: RangeArgument<usize> { | ||
// Memory safety | ||
// | ||
// When the Drain is first created, it shortens the length of | ||
// the source vector to make sure no uninitalized or moved-from elements | ||
// are accessible at all if the Drain's destructor never gets to run. | ||
// | ||
// Drain will ptr::read out the values to remove. | ||
// When finished, remaining tail of the vec is copied back to cover | ||
// the hole, and the vector length is restored to the new length. | ||
// | ||
let len = self.len(); | ||
let start = *range.start().unwrap_or(&0); | ||
let end = *range.end().unwrap_or(&len); | ||
assert!(start <= end); | ||
assert!(end <= len); | ||
|
||
unsafe { | ||
let begin = *self.ptr as *const T; | ||
let end = if mem::size_of::<T>() == 0 { | ||
(*self.ptr as usize + self.len()) as *const T | ||
} else { | ||
(*self.ptr).offset(self.len() as isize) as *const T | ||
}; | ||
self.set_len(0); | ||
// set self.vec length's to start, to be safe in case Drain is leaked | ||
self.set_len(start); | ||
// Use the borrow in the IterMut to indicate borrowing behavior of the | ||
// whole Drain iterator (like &mut T). | ||
let range_slice = slice::from_raw_parts_mut( | ||
self.as_mut_ptr().offset(start as isize), | ||
end - start); | ||
Drain { | ||
ptr: begin, | ||
end: end, | ||
marker: PhantomData, | ||
tail_start: end, | ||
tail_len: len - end, | ||
iter: range_slice.iter_mut(), | ||
vec: self as *mut _, | ||
} | ||
} | ||
} | ||
|
@@ -1799,14 +1826,16 @@ impl<T> Drop for IntoIter<T> { | |
} | ||
} | ||
|
||
/// An iterator that drains a vector. | ||
#[unsafe_no_drop_flag] | ||
#[unstable(feature = "collections", | ||
reason = "recently added as part of collections reform 2")] | ||
pub struct Drain<'a, T:'a> { | ||
ptr: *const T, | ||
end: *const T, | ||
marker: PhantomData<&'a T>, | ||
/// A draining iterator for `Vec<T>`. | ||
#[unstable(feature = "collections_drain", reason = "recently added")] | ||
pub struct Drain<'a, T: 'a> { | ||
/// Index of tail to preserve | ||
tail_start: usize, | ||
/// Length of tail | ||
tail_len: usize, | ||
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. In the interest of saving space, either length or start can be derived from the other, right? 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. Oh wait nevermind, the vector's length is "inaccurate" so it can't be used! 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. The vector's temporary length stores another necessary field -- the original starting point of the range we are removing. 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. Space that can be saved: The drop flag. 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'm not going to attempt the drop flag without advice because I'm not up to date with how filling drop interacts with that. |
||
/// Current remaining range to remove | ||
iter: slice::IterMut<'a, T>, | ||
vec: *mut Vec<T>, | ||
} | ||
|
||
unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {} | ||
|
@@ -1818,76 +1847,56 @@ impl<'a, T> Iterator 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 usize + 1); | ||
|
||
// Use a non-null pointer value | ||
Some(ptr::read(EMPTY as *mut T)) | ||
} else { | ||
let old = self.ptr; | ||
self.ptr = self.ptr.offset(1); | ||
|
||
Some(ptr::read(old)) | ||
} | ||
self.iter.next().map(|elt| | ||
unsafe { | ||
ptr::read(elt as *const _) | ||
} | ||
} | ||
) | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
let diff = (self.end as usize) - (self.ptr as usize); | ||
let size = mem::size_of::<T>(); | ||
let exact = diff / (if size == 0 {1} else {size}); | ||
(exact, Some(exact)) | ||
self.iter.size_hint() | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<'a, T> DoubleEndedIterator 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 usize - 1); | ||
|
||
// Use a non-null pointer value | ||
Some(ptr::read(EMPTY as *mut T)) | ||
} else { | ||
self.end = self.end.offset(-1); | ||
|
||
Some(ptr::read(self.end)) | ||
} | ||
self.iter.next_back().map(|elt| | ||
unsafe { | ||
ptr::read(elt as *const _) | ||
} | ||
} | ||
) | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<'a, T> ExactSizeIterator for Drain<'a, T> {} | ||
|
||
#[unsafe_destructor] | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<'a, T> Drop for Drain<'a, T> { | ||
fn drop(&mut self) { | ||
// self.ptr == self.end == mem::POST_DROP_USIZE if drop has already been called, | ||
// so we can use #[unsafe_no_drop_flag]. | ||
// exhaust self first | ||
while let Some(_) = self.next() { } | ||
|
||
// destroy the remaining elements | ||
for _x in self.by_ref() {} | ||
if self.tail_len > 0 { | ||
unsafe { | ||
let source_vec = &mut *self.vec; | ||
// memmove back untouched tail, update to new length | ||
let start = source_vec.len(); | ||
let tail = self.tail_start; | ||
let src = source_vec.as_ptr().offset(tail as isize); | ||
let dst = source_vec.as_mut_ptr().offset(start as isize); | ||
ptr::copy(src, dst, self.tail_len); | ||
source_vec.set_len(start + self.tail_len); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<'a, T> ExactSizeIterator for Drain<'a, T> {} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Conversion from &[T] to &Vec<T> | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
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.
Props for this, it's a clever solution!