diff --git a/src/lib.rs b/src/lib.rs index 5fc7ba5..cd3f2e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,9 @@ use std::hash::{Hash, Hasher}; use std::fmt; mod array; +mod misc; pub use array::Array; +pub use misc::RangeArgument; unsafe fn new_array() -> A { @@ -169,9 +171,7 @@ impl ArrayVec { /// /// let mut array = ArrayVec::from([1, 2, 3]); /// - /// let elt = array.swap_remove(0); - /// - /// assert_eq!(elt, Some(1)); + /// assert_eq!(array.swap_remove(0), Some(1)); /// assert_eq!(&array[..], &[3, 2]); /// /// assert_eq!(array.swap_remove(10), None); @@ -184,6 +184,80 @@ impl ArrayVec { self.swap(index, len - 1); 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 { + 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(&mut self, range: R) -> Drain { + // 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 Deref for ArrayVec { @@ -283,7 +357,7 @@ impl IntoIterator for ArrayVec { } -/// By-value iterator for ArrayVec. +/// By-value iterator for **ArrayVec**. pub struct IntoIter { index: u8, v: ArrayVec, @@ -338,6 +412,83 @@ impl Drop for IntoIter { } } +/// 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, +} + +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.iter.next().map(|elt| + unsafe { + ptr::read(elt as *const _) + } + ) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + 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.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. /// /// Does not extract more items than there is space for. No error @@ -520,3 +671,28 @@ fn test_compact_size() { println!("{}", mem::size_of::()); assert!(mem::size_of::() <= 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); +} diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..d4bc56f --- /dev/null +++ b/src/misc.rs @@ -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 { None } + #[doc(hidden)] + /// End index (exclusive) + fn end(&self) -> Option { None } +} + + +impl RangeArgument for RangeFull {} + +impl RangeArgument for RangeFrom { + fn start(&self) -> Option { Some(self.start) } +} + +impl RangeArgument for RangeTo { + fn end(&self) -> Option { Some(self.end) } +} + +impl RangeArgument for Range { + fn start(&self) -> Option { Some(self.start) } + fn end(&self) -> Option { Some(self.end) } +} +