Skip to content

Commit 626fdbc

Browse files
committed
add fn make_contiguous to VecDeque
1 parent 303d8af commit 626fdbc

File tree

2 files changed

+227
-53
lines changed

2 files changed

+227
-53
lines changed

src/liballoc/collections/vec_deque.rs

+145-52
Original file line numberDiff line numberDiff line change
@@ -2043,6 +2043,146 @@ impl<T> VecDeque<T> {
20432043
}
20442044
}
20452045

2046+
/// Rearranges the internal storage of this deque so it is one contiguous slice, which is then returned.
2047+
///
2048+
/// This method does not allocate and does not change the order of the inserted elements.
2049+
/// As it returns a mutable slice, this can be used to sort or binary search a deque.
2050+
///
2051+
/// In case `self` is already contiguous, [`as_slices`](#method.as_slices) can be used to get immutable access.
2052+
///
2053+
/// # Examples
2054+
///
2055+
/// Sorting the content of a deque.
2056+
///
2057+
/// ```
2058+
/// #![feature(deque_make_contiguous)]
2059+
///
2060+
/// use std::collections::VecDeque;
2061+
///
2062+
/// let mut buf = VecDeque::with_capacity(15);
2063+
///
2064+
/// buf.push_back(2);
2065+
/// buf.push_back(1);
2066+
/// buf.push_front(3);
2067+
///
2068+
/// // sorting the deque
2069+
/// buf.make_contiguous().sort();
2070+
/// assert_eq!(buf.as_slices(), (&[1, 2, 3] as &[_], &[] as &[_]));
2071+
///
2072+
/// // sorting it in reverse order
2073+
/// buf.make_contiguous().sort_by(|a, b| b.cmp(a));
2074+
/// assert_eq!(buf.as_slices(), (&[3, 2, 1] as &[_], &[] as &[_]));
2075+
/// ```
2076+
///
2077+
/// Getting immutable access to the contiguous slice.
2078+
///
2079+
/// ```rust
2080+
/// #![feature(deque_make_contiguous)]
2081+
///
2082+
/// use std::collections::VecDeque;
2083+
///
2084+
/// let mut buf = VecDeque::new();
2085+
///
2086+
/// buf.push_back(2);
2087+
/// buf.push_back(1);
2088+
/// buf.push_front(3);
2089+
///
2090+
/// buf.make_contiguous();
2091+
/// if let (slice, &[]) = buf.as_slices() {
2092+
/// // we can now be sure that `slice` contains all elements of the deque,
2093+
/// // while still having immutable access to `buf`.
2094+
/// assert_eq!(buf.len(), slice.len());
2095+
/// assert_eq!(slice, &[3, 2, 1] as &[_]);
2096+
/// }
2097+
/// ```
2098+
#[unstable(feature = "deque_make_contiguous", issue = "none")]
2099+
pub fn make_contiguous(&mut self) -> &mut [T] {
2100+
if self.is_contiguous() {
2101+
let tail = self.tail;
2102+
let head = self.head;
2103+
return unsafe { &mut self.buffer_as_mut_slice()[tail..head] };
2104+
}
2105+
2106+
let buf = self.buf.ptr();
2107+
let cap = self.cap();
2108+
let len = self.len();
2109+
2110+
let free = self.tail - self.head;
2111+
let tail_len = cap - self.tail;
2112+
2113+
if free >= tail_len {
2114+
// there is enough free space to copy the tail in one go,
2115+
// this means that we first shift the head backwards, and then
2116+
// copy the tail to the correct position.
2117+
//
2118+
// from: DEFGH....ABC
2119+
// to: ABCDEFGH....
2120+
unsafe {
2121+
ptr::copy(buf, buf.add(tail_len), self.head);
2122+
// ...DEFGH.ABC
2123+
ptr::copy_nonoverlapping(buf.add(self.tail), buf, tail_len);
2124+
// ABCDEFGH....
2125+
2126+
self.tail = 0;
2127+
self.head = len;
2128+
}
2129+
} else if free >= self.head {
2130+
// there is enough free space to copy the head in one go,
2131+
// this means that we first shift the tail forwards, and then
2132+
// copy the head to the correct position.
2133+
//
2134+
// from: FGH....ABCDE
2135+
// to: ...ABCDEFGH.
2136+
unsafe {
2137+
ptr::copy(buf.add(self.tail), buf.add(self.head), tail_len);
2138+
// FGHABCDE....
2139+
ptr::copy_nonoverlapping(buf, buf.add(self.head + tail_len), self.head);
2140+
// ...ABCDEFGH.
2141+
2142+
self.tail = self.head;
2143+
self.head = self.tail + len;
2144+
}
2145+
} else {
2146+
// free is smaller than both head and tail,
2147+
// this means we have to slowly "swap" the tail and the head.
2148+
//
2149+
// from: EFGHI...ABCD or HIJK.ABCDEFG
2150+
// to: ABCDEFGHI... or ABCDEFGHIJK.
2151+
let mut left_edge: usize = 0;
2152+
let mut right_edge: usize = self.tail;
2153+
unsafe {
2154+
// The general problem looks like this
2155+
// GHIJKLM...ABCDEF - before any swaps
2156+
// ABCDEFM...GHIJKL - after 1 pass of swaps
2157+
// ABCDEFGHIJM...KL - swap until the left edge reaches the temp store
2158+
// - then restart the algorithm with a new (smaller) store
2159+
// Sometimes the temp store is reached when the right edge is at the end
2160+
// of the buffer - this means we've hit the right order with fewer swaps!
2161+
// E.g
2162+
// EF..ABCD
2163+
// ABCDEF.. - after four only swaps we've finished
2164+
while left_edge < len && right_edge != cap {
2165+
let mut right_offset = 0;
2166+
for i in left_edge..right_edge {
2167+
right_offset = (i - left_edge) % (cap - right_edge);
2168+
let src: isize = (right_edge + right_offset) as isize;
2169+
ptr::swap(buf.add(i), buf.offset(src));
2170+
}
2171+
let n_ops = right_edge - left_edge;
2172+
left_edge += n_ops;
2173+
right_edge += right_offset + 1;
2174+
}
2175+
2176+
self.tail = 0;
2177+
self.head = len;
2178+
}
2179+
}
2180+
2181+
let tail = self.tail;
2182+
let head = self.head;
2183+
unsafe { &mut self.buffer_as_mut_slice()[tail..head] }
2184+
}
2185+
20462186
/// Rotates the double-ended queue `mid` places to the left.
20472187
///
20482188
/// Equivalently,
@@ -2802,63 +2942,16 @@ impl<T> From<VecDeque<T>> for Vec<T> {
28022942
/// assert_eq!(vec, [8, 9, 1, 2, 3, 4]);
28032943
/// assert_eq!(vec.as_ptr(), ptr);
28042944
/// ```
2805-
fn from(other: VecDeque<T>) -> Self {
2945+
fn from(mut other: VecDeque<T>) -> Self {
2946+
other.make_contiguous();
2947+
28062948
unsafe {
28072949
let buf = other.buf.ptr();
28082950
let len = other.len();
2809-
let tail = other.tail;
2810-
let head = other.head;
28112951
let cap = other.cap();
28122952

2813-
// Need to move the ring to the front of the buffer, as vec will expect this.
2814-
if other.is_contiguous() {
2815-
ptr::copy(buf.add(tail), buf, len);
2816-
} else {
2817-
if (tail - head) >= cmp::min(cap - tail, head) {
2818-
// There is enough free space in the centre for the shortest block so we can
2819-
// do this in at most three copy moves.
2820-
if (cap - tail) > head {
2821-
// right hand block is the long one; move that enough for the left
2822-
ptr::copy(buf.add(tail), buf.add(tail - head), cap - tail);
2823-
// copy left in the end
2824-
ptr::copy(buf, buf.add(cap - head), head);
2825-
// shift the new thing to the start
2826-
ptr::copy(buf.add(tail - head), buf, len);
2827-
} else {
2828-
// left hand block is the long one, we can do it in two!
2829-
ptr::copy(buf, buf.add(cap - tail), head);
2830-
ptr::copy(buf.add(tail), buf, cap - tail);
2831-
}
2832-
} else {
2833-
// Need to use N swaps to move the ring
2834-
// We can use the space at the end of the ring as a temp store
2835-
2836-
let mut left_edge: usize = 0;
2837-
let mut right_edge: usize = tail;
2838-
2839-
// The general problem looks like this
2840-
// GHIJKLM...ABCDEF - before any swaps
2841-
// ABCDEFM...GHIJKL - after 1 pass of swaps
2842-
// ABCDEFGHIJM...KL - swap until the left edge reaches the temp store
2843-
// - then restart the algorithm with a new (smaller) store
2844-
// Sometimes the temp store is reached when the right edge is at the end
2845-
// of the buffer - this means we've hit the right order with fewer swaps!
2846-
// E.g
2847-
// EF..ABCD
2848-
// ABCDEF.. - after four only swaps we've finished
2849-
2850-
while left_edge < len && right_edge != cap {
2851-
let mut right_offset = 0;
2852-
for i in left_edge..right_edge {
2853-
right_offset = (i - left_edge) % (cap - right_edge);
2854-
let src: isize = (right_edge + right_offset) as isize;
2855-
ptr::swap(buf.add(i), buf.offset(src));
2856-
}
2857-
let n_ops = right_edge - left_edge;
2858-
left_edge += n_ops;
2859-
right_edge += right_offset + 1;
2860-
}
2861-
}
2953+
if other.head != 0 {
2954+
ptr::copy(buf.add(other.tail), buf, len);
28622955
}
28632956
let out = Vec::from_raw_parts(buf, len, cap);
28642957
mem::forget(other);

src/liballoc/collections/vec_deque/tests.rs

+82-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
22

3-
use ::test;
3+
use test;
44

55
#[bench]
66
#[cfg_attr(miri, ignore)] // Miri does not support benchmarks
@@ -130,6 +130,87 @@ fn test_insert() {
130130
}
131131
}
132132

133+
#[test]
134+
fn make_contiguous_big_tail() {
135+
let mut tester = VecDeque::with_capacity(15);
136+
137+
for i in 0..3 {
138+
tester.push_back(i);
139+
}
140+
141+
for i in 3..10 {
142+
tester.push_front(i);
143+
}
144+
145+
// 012......9876543
146+
assert_eq!(tester.capacity(), 15);
147+
assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices());
148+
149+
let expected_start = tester.head;
150+
tester.make_contiguous();
151+
assert_eq!(tester.tail, expected_start);
152+
assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices());
153+
}
154+
155+
#[test]
156+
fn make_contiguous_big_head() {
157+
let mut tester = VecDeque::with_capacity(15);
158+
159+
for i in 0..8 {
160+
tester.push_back(i);
161+
}
162+
163+
for i in 8..10 {
164+
tester.push_front(i);
165+
}
166+
167+
// 01234567......98
168+
let expected_start = 0;
169+
tester.make_contiguous();
170+
assert_eq!(tester.tail, expected_start);
171+
assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices());
172+
}
173+
174+
#[test]
175+
fn make_contiguous_small_free() {
176+
let mut tester = VecDeque::with_capacity(15);
177+
178+
for i in 'A' as u8..'I' as u8 {
179+
tester.push_back(i as char);
180+
}
181+
182+
for i in 'I' as u8..'N' as u8 {
183+
tester.push_front(i as char);
184+
}
185+
186+
// ABCDEFGH...MLKJI
187+
let expected_start = 0;
188+
tester.make_contiguous();
189+
assert_eq!(tester.tail, expected_start);
190+
assert_eq!(
191+
(&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]),
192+
tester.as_slices()
193+
);
194+
195+
tester.clear();
196+
for i in 'I' as u8..'N' as u8 {
197+
tester.push_back(i as char);
198+
}
199+
200+
for i in 'A' as u8..'I' as u8 {
201+
tester.push_front(i as char);
202+
}
203+
204+
// IJKLM...HGFEDCBA
205+
let expected_start = 0;
206+
tester.make_contiguous();
207+
assert_eq!(tester.tail, expected_start);
208+
assert_eq!(
209+
(&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]),
210+
tester.as_slices()
211+
);
212+
}
213+
133214
#[test]
134215
fn test_remove() {
135216
// This test checks that every single combination of tail position, length, and

0 commit comments

Comments
 (0)