Implement ArrayVec::remove() and ::drain()

This commit is contained in:
root
2015-05-22 14:11:00 +02:00
parent 33e31b8ecf
commit 8f8db4a3ed
2 changed files with 214 additions and 4 deletions
+180 -4
View File
@@ -17,7 +17,9 @@ use std::hash::{Hash, Hasher};
use std::fmt; use std::fmt;
mod array; mod array;
mod misc;
pub use array::Array; pub use array::Array;
pub use misc::RangeArgument;
unsafe fn new_array<A: Array>() -> A { unsafe fn new_array<A: Array>() -> A {
@@ -169,9 +171,7 @@ impl<A: Array> ArrayVec<A> {
/// ///
/// let mut array = ArrayVec::from([1, 2, 3]); /// let mut array = ArrayVec::from([1, 2, 3]);
/// ///
/// let elt = array.swap_remove(0); /// assert_eq!(array.swap_remove(0), Some(1));
///
/// assert_eq!(elt, Some(1));
/// assert_eq!(&array[..], &[3, 2]); /// assert_eq!(&array[..], &[3, 2]);
/// ///
/// assert_eq!(array.swap_remove(10), None); /// assert_eq!(array.swap_remove(10), None);
@@ -184,6 +184,80 @@ impl<A: Array> ArrayVec<A> {
self.swap(index, len - 1); self.swap(index, len - 1);
self.pop() self.pop()
} }
/// Remove the element at **index** and shift the following elements down.
///
/// Return **Some(** *element* **)** if the index is in bounds, else **None**.
///
/// ## Examples
/// ```
/// use arrayvec::ArrayVec;
///
/// let mut array = ArrayVec::from([1, 2, 3]);
///
/// assert_eq!(array.remove(0), Some(1));
/// assert_eq!(&array[..], &[2, 3]);
///
/// assert_eq!(array.remove(10), None);
/// ```
pub fn remove(&mut self, index: usize) -> Option<A::Item> {
if index >= self.len() {
None
} else {
self.drain(index..index + 1).next()
}
}
/// 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
///
/// ```
/// use arrayvec::ArrayVec;
///
/// let mut v = ArrayVec::from([1, 2, 3]);
/// let u: Vec<_> = v.drain(0..2).collect();
/// assert_eq!(&v[..], &[3]);
/// assert_eq!(&u[..], &[1, 2]);
/// ```
pub fn drain<R: RangeArgument>(&mut self, range: R) -> Drain<A> {
// 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);
// bounds check happens here
let range_slice: *const _ = &self[start..end];
unsafe {
// set self.vec length's to start, to be safe in case Drain is leaked
self.len = start as u8;
Drain {
tail_start: end,
tail_len: len - end,
iter: (*range_slice).iter(),
vec: self as *mut _,
}
}
}
} }
impl<A: Array> Deref for ArrayVec<A> { impl<A: Array> Deref for ArrayVec<A> {
@@ -283,7 +357,7 @@ impl<A: Array> IntoIterator for ArrayVec<A> {
} }
/// By-value iterator for ArrayVec. /// By-value iterator for **ArrayVec**.
pub struct IntoIter<A: Array> { pub struct IntoIter<A: Array> {
index: u8, index: u8,
v: ArrayVec<A>, v: ArrayVec<A>,
@@ -338,6 +412,83 @@ impl<A: Array> Drop for IntoIter<A> {
} }
} }
/// A draining iterator for **ArrayVec**.
pub struct Drain<'a, A>
where A: Array,
A::Item: 'a,
{
/// Index of tail to preserve
tail_start: usize,
/// Length of tail
tail_len: usize,
/// Current remaining range to remove
iter: slice::Iter<'a, A::Item>,
vec: *mut ArrayVec<A>,
}
unsafe impl<'a, A: Array + Sync> Sync for Drain<'a, A> {}
unsafe impl<'a, A: Array + Send> Send for Drain<'a, A> {}
impl<'a, A: Array> Iterator for Drain<'a, A>
where A::Item: 'a,
{
type Item = A::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|elt|
unsafe {
ptr::read(elt as *const _)
}
)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, A: Array> DoubleEndedIterator for Drain<'a, A>
where A::Item: 'a,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|elt|
unsafe {
ptr::read(elt as *const _)
}
)
}
}
impl<'a, A: Array> ExactSizeIterator for Drain<'a, A> where A::Item: 'a {}
impl<'a, A: Array> Drop for Drain<'a, A>
where A::Item: 'a
{
fn drop(&mut self) {
// exhaust self first
while let Some(_) = self.next() { }
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.len = (start + self.tail_len) as u8;
}
}
}
}
/// Extend the **ArrayVec** with an iterator. /// Extend the **ArrayVec** with an iterator.
/// ///
/// Does not extract more items than there is space for. No error /// Does not extract more items than there is space for. No error
@@ -520,3 +671,28 @@ fn test_compact_size() {
println!("{}", mem::size_of::<QuadArray>()); println!("{}", mem::size_of::<QuadArray>());
assert!(mem::size_of::<QuadArray>() <= 24); assert!(mem::size_of::<QuadArray>() <= 24);
} }
#[test]
fn test_drain() {
let mut v = ArrayVec::from([0; 8]);
v.pop();
v.drain(0..7);
assert_eq!(&v[..], &[]);
v.extend(0..);
v.drain(1..4);
assert_eq!(&v[..], &[0, 4, 5, 6, 7]);
let u: ArrayVec<[_; 3]> = v.drain(1..4).rev().collect();
assert_eq!(&u[..], &[6, 5, 4]);
assert_eq!(&v[..], &[0, 7]);
v.drain(..);
assert_eq!(&v[..], &[]);
}
#[test]
#[should_panic]
fn test_drain_oob() {
let mut v = ArrayVec::from([0; 8]);
v.pop();
v.drain(0..8);
}
+34
View File
@@ -0,0 +1,34 @@
use std::ops::{
RangeFull,
RangeFrom,
RangeTo,
Range,
};
/// **RangeArgument** is implemented by Rust's built-in range types, produced
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
pub trait RangeArgument {
#[doc(hidden)]
/// Start index (inclusive)
fn start(&self) -> Option<usize> { None }
#[doc(hidden)]
/// End index (exclusive)
fn end(&self) -> Option<usize> { None }
}
impl RangeArgument for RangeFull {}
impl RangeArgument for RangeFrom<usize> {
fn start(&self) -> Option<usize> { Some(self.start) }
}
impl RangeArgument for RangeTo<usize> {
fn end(&self) -> Option<usize> { Some(self.end) }
}
impl RangeArgument for Range<usize> {
fn start(&self) -> Option<usize> { Some(self.start) }
fn end(&self) -> Option<usize> { Some(self.end) }
}