Skip to content

Commit

Permalink
Rollup merge of rust-lang#74099 - jonhoo:deque-range, r=dtolnay
Browse files Browse the repository at this point in the history
Add VecDeque::range* methods

This patch adds `VecDeque::range` and `VecDeque::range_mut` to provide
iterators over a sub-range of a `VecDeque`. This behavior can be
emulated with `skip` and `take`, but directly providing a `Range` is
more ergonomic. This also partially makes up for `VecDeque`'s lack of
`SliceIndex` support.
  • Loading branch information
Manishearth authored Jul 11, 2020
2 parents b424009 + a1a19cb commit 6a1d414
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 13 deletions.
116 changes: 103 additions & 13 deletions src/liballoc/collections/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,108 @@ impl<T> VecDeque<T> {
self.tail == self.head
}

fn range_start_end<R>(&self, range: R) -> (usize, usize)
where
R: RangeBounds<usize>,
{
let len = self.len();
let start = match range.start_bound() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end_bound() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
assert!(start <= end, "lower bound was too large");
assert!(end <= len, "upper bound was too large");
(start, end)
}

/// Creates an iterator that covers the specified range in the `VecDeque`.
///
/// # 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(deque_range)]
///
/// use std::collections::VecDeque;
///
/// let v: VecDeque<_> = vec![1, 2, 3].into_iter().collect();
/// let range = v.range(2..).copied().collect::<VecDeque<_>>();
/// assert_eq!(range, [3]);
///
/// // A full range covers all contents
/// let all = v.range(..);
/// assert_eq!(all.len(), 3);
/// ```
#[inline]
#[unstable(feature = "deque_range", issue = "74217")]
pub fn range<R>(&self, range: R) -> Iter<'_, T>
where
R: RangeBounds<usize>,
{
let (start, end) = self.range_start_end(range);
let tail = self.wrap_add(self.tail, start);
let head = self.wrap_add(self.tail, end);
Iter {
tail,
head,
// The shared reference we have in &self is maintained in the '_ of Iter.
ring: unsafe { self.buffer_as_slice() },
}
}

/// Creates an iterator that covers the specified mutable range in the `VecDeque`.
///
/// # 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(deque_range)]
///
/// use std::collections::VecDeque;
///
/// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect();
/// for v in v.range_mut(2..) {
/// *v *= 2;
/// }
/// assert_eq!(v, vec![1, 2, 6]);
///
/// // A full range covers all contents
/// for v in v.range_mut(..) {
/// *v *= 2;
/// }
/// assert_eq!(v, vec![2, 4, 12]);
/// ```
#[inline]
#[unstable(feature = "deque_range", issue = "74217")]
pub fn range_mut<R>(&mut self, range: R) -> IterMut<'_, T>
where
R: RangeBounds<usize>,
{
let (start, end) = self.range_start_end(range);
let tail = self.wrap_add(self.tail, start);
let head = self.wrap_add(self.tail, end);
IterMut {
tail,
head,
// The shared reference we have in &mut self is maintained in the '_ of IterMut.
ring: unsafe { self.buffer_as_mut_slice() },
}
}

/// Creates a draining iterator that removes the specified range in the
/// `VecDeque` and yields the removed items.
///
Expand Down Expand Up @@ -1129,19 +1231,7 @@ impl<T> VecDeque<T> {
// When finished, the remaining data will be copied back to cover the hole,
// and the head/tail values will be restored correctly.
//
let len = self.len();
let start = match range.start_bound() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end_bound() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
assert!(start <= end, "drain lower bound was too large");
assert!(end <= len, "drain upper bound was too large");
let (start, end) = self.range_start_end(range);

// The deque's elements are parted into three segments:
// * self.tail -> drain_tail
Expand Down
59 changes: 59 additions & 0 deletions src/liballoc/collections/vec_deque/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,65 @@ fn test_remove() {
}
}

#[test]
fn test_range() {
let mut tester: VecDeque<usize> = VecDeque::with_capacity(7);

let cap = tester.capacity();
for len in 0..=cap {
for tail in 0..=cap {
for start in 0..=len {
for end in start..=len {
tester.tail = tail;
tester.head = tail;
for i in 0..len {
tester.push_back(i);
}

// Check that we iterate over the correct values
let range: VecDeque<_> = tester.range(start..end).copied().collect();
let expected: VecDeque<_> = (start..end).collect();
assert_eq!(range, expected);
}
}
}
}
}

#[test]
fn test_range_mut() {
let mut tester: VecDeque<usize> = VecDeque::with_capacity(7);

let cap = tester.capacity();
for len in 0..=cap {
for tail in 0..=cap {
for start in 0..=len {
for end in start..=len {
tester.tail = tail;
tester.head = tail;
for i in 0..len {
tester.push_back(i);
}

let head_was = tester.head;
let tail_was = tester.tail;

// Check that we iterate over the correct values
let range: VecDeque<_> = tester.range_mut(start..end).map(|v| *v).collect();
let expected: VecDeque<_> = (start..end).collect();
assert_eq!(range, expected);

// We shouldn't have changed the capacity or made the
// head or tail out of bounds
assert_eq!(tester.capacity(), cap);
assert_eq!(tester.tail, tail_was);
assert_eq!(tester.head, head_was);
}
}
}
}
}

#[test]
fn test_drain() {
let mut tester: VecDeque<usize> = VecDeque::with_capacity(7);
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
#![feature(const_in_array_repeat_expressions)]
#![cfg_attr(bootstrap, feature(const_if_match))]
#![feature(cow_is_borrowed)]
#![feature(deque_range)]
#![feature(dispatch_from_dyn)]
#![feature(core_intrinsics)]
#![feature(container_error_extra)]
Expand Down

0 comments on commit 6a1d414

Please sign in to comment.