Skip to content

Commit f62cfa7

Browse files
authored
Rollup merge of #69425 - lcnr:make_contiguous, r=Amanieu
add fn make_contiguous to VecDeque Adds the following method to VecDeque: ```rust pub fn make_contiguous(&mut self) -> &mut [T]; ``` Taken from #69400, after a suggestion by @CryZe #69400 (comment) I am in favor of merging this instead of #69400.
2 parents 75ff311 + e1afd26 commit f62cfa7

File tree

2 files changed

+235
-53
lines changed

2 files changed

+235
-53
lines changed

src/liballoc/collections/vec_deque.rs

+153-52
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,9 @@ impl<T> VecDeque<T> {
959959
/// Returns a pair of slices which contain, in order, the contents of the
960960
/// `VecDeque`.
961961
///
962+
/// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements
963+
/// of the `VecDeque` will be in the first slice and the second slice will be empty.
964+
///
962965
/// # Examples
963966
///
964967
/// ```
@@ -989,6 +992,9 @@ impl<T> VecDeque<T> {
989992
/// Returns a pair of slices which contain, in order, the contents of the
990993
/// `VecDeque`.
991994
///
995+
/// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements
996+
/// of the `VecDeque` will be in the first slice and the second slice will be empty.
997+
///
992998
/// # Examples
993999
///
9941000
/// ```
@@ -2044,6 +2050,148 @@ impl<T> VecDeque<T> {
20442050
}
20452051
}
20462052

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

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