Skip to content

Commit 2032502

Browse files
committed
Adds make_contiguous to Deque
1 parent b1f040d commit 2032502

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed

src/deque.rs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,178 @@ impl<T, const N: usize> Deque<T, N> {
191191
}
192192
}
193193

194+
#[inline]
195+
fn is_contiguous(&self) -> bool {
196+
self.front <= N - self.len()
197+
}
198+
199+
/// Rearranges the internal storage of the [`Deque`] to make it into a contiguous slice,
200+
/// which is returned.
201+
///
202+
/// This does **not** change the order of the elements in the deque.
203+
/// The returned slice can then be used to perform contiguous slice operations on the deque.
204+
///
205+
/// After calling this method, subsequent [`as_slices`] and [`as_mut_slices`] calls will return
206+
/// a single contiguous slice.
207+
///
208+
/// [`as_slices`]: Deque::as_slices
209+
/// [`as_mut_slices`]: Deque::as_mut_slices
210+
pub fn make_contiguous(&mut self) -> &mut [T] {
211+
if self.is_contiguous() {
212+
return unsafe {
213+
slice::from_raw_parts_mut(
214+
self.buffer.as_mut_ptr().add(self.front).cast(),
215+
self.len(),
216+
)
217+
};
218+
}
219+
220+
let len = self.len();
221+
222+
let free = N - len;
223+
let front_len = N - self.front;
224+
let back = len - front_len;
225+
let back_len = back;
226+
227+
if free >= front_len {
228+
// there is enough free space to copy the head in one go,
229+
// this means that we first shift the tail backwards, and then
230+
// copy the head to the correct position.
231+
//
232+
// from: DEFGH....ABC
233+
// to: ABCDEFGH....
234+
unsafe {
235+
ptr::copy(
236+
self.buffer.as_ptr(),
237+
self.buffer.as_mut_ptr().add(front_len),
238+
back_len,
239+
);
240+
// ...DEFGH.ABC
241+
ptr::copy_nonoverlapping(
242+
self.buffer.as_ptr().add(self.front),
243+
self.buffer.as_mut_ptr(),
244+
front_len,
245+
);
246+
// ABCDEFGH....
247+
}
248+
249+
self.front = 0;
250+
self.back = len;
251+
} else if free >= back_len {
252+
// there is enough free space to copy the tail in one go,
253+
// this means that we first shift the head forwards, and then
254+
// copy the tail to the correct position.
255+
//
256+
// from: FGH....ABCDE
257+
// to: ...ABCDEFGH.
258+
unsafe {
259+
ptr::copy(
260+
self.buffer.as_ptr().add(self.front),
261+
self.buffer.as_mut_ptr().add(self.back),
262+
front_len,
263+
);
264+
// FGHABCDE....
265+
ptr::copy_nonoverlapping(
266+
self.buffer.as_ptr(),
267+
self.buffer.as_mut_ptr().add(self.back + front_len),
268+
back_len,
269+
);
270+
// ...ABCDEFGH.
271+
}
272+
273+
self.front = back;
274+
self.back = 0;
275+
} else {
276+
// `free` is smaller than both `head_len` and `tail_len`.
277+
// the general algorithm for this first moves the slices
278+
// right next to each other and then uses `slice::rotate`
279+
// to rotate them into place:
280+
//
281+
// initially: HIJK..ABCDEFG
282+
// step 1: ..HIJKABCDEFG
283+
// step 2: ..ABCDEFGHIJK
284+
//
285+
// or:
286+
//
287+
// initially: FGHIJK..ABCDE
288+
// step 1: FGHIJKABCDE..
289+
// step 2: ABCDEFGHIJK..
290+
291+
// pick the shorter of the 2 slices to reduce the amount
292+
// of memory that needs to be moved around.
293+
if front_len > back_len {
294+
// tail is shorter, so:
295+
// 1. copy tail forwards
296+
// 2. rotate used part of the buffer
297+
// 3. update head to point to the new beginning (which is just `free`)
298+
unsafe {
299+
// if there is no free space in the buffer, then the slices are already
300+
// right next to each other and we don't need to move any memory.
301+
if free != 0 {
302+
// because we only move the tail forward as much as there's free space
303+
// behind it, we don't overwrite any elements of the head slice, and
304+
// the slices end up right next to each other.
305+
ptr::copy(
306+
self.buffer.as_ptr(),
307+
self.buffer.as_mut_ptr().add(free),
308+
back_len,
309+
);
310+
}
311+
312+
// We just copied the tail right next to the head slice,
313+
// so all of the elements in the range are initialized
314+
let slice: &mut [T] = slice::from_raw_parts_mut(
315+
self.buffer.as_mut_ptr().add(free).cast(),
316+
N - free,
317+
);
318+
319+
// because the deque wasn't contiguous, we know that `tail_len < self.len == slice.len()`,
320+
// so this will never panic.
321+
slice.rotate_left(back_len);
322+
323+
// the used part of the buffer now is `free..self.capacity()`, so set
324+
// `head` to the beginning of that range.
325+
self.front = free;
326+
self.back = 0;
327+
}
328+
} else {
329+
// head is shorter so:
330+
// 1. copy head backwards
331+
// 2. rotate used part of the buffer
332+
// 3. update head to point to the new beginning (which is the beginning of the buffer)
333+
334+
unsafe {
335+
// if there is no free space in the buffer, then the slices are already
336+
// right next to each other and we don't need to move any memory.
337+
if free != 0 {
338+
// copy the head slice to lie right behind the tail slice.
339+
ptr::copy(
340+
self.buffer.as_ptr().add(self.front),
341+
self.buffer.as_mut_ptr().add(back_len),
342+
front_len,
343+
);
344+
}
345+
346+
// because we copied the head slice so that both slices lie right
347+
// next to each other, all the elements in the range are initialized.
348+
let slice: &mut [T] =
349+
slice::from_raw_parts_mut(self.buffer.as_mut_ptr().cast(), len);
350+
351+
// because the deque wasn't contiguous, we know that `head_len < self.len == slice.len()`
352+
// so this will never panic.
353+
slice.rotate_right(front_len);
354+
355+
// the used part of the buffer now is `0..self.len`, so set
356+
// `head` to the beginning of that range.
357+
self.front = 0;
358+
self.back = len;
359+
}
360+
}
361+
}
362+
363+
unsafe { slice::from_raw_parts_mut(self.buffer.as_mut_ptr().add(self.front).cast(), len) }
364+
}
365+
194366
/// Provides a reference to the front element, or None if the `Deque` is empty.
195367
pub fn front(&self) -> Option<&T> {
196368
if self.is_empty() {
@@ -866,4 +1038,55 @@ mod tests {
8661038
q.push_back(0).unwrap();
8671039
assert_eq!(q.len(), 1);
8681040
}
1041+
1042+
#[test]
1043+
fn make_contiguous() {
1044+
let mut q: Deque<i32, 4> = Deque::new();
1045+
assert_eq!(q.len(), 0);
1046+
1047+
q.push_back(0).unwrap();
1048+
q.push_back(1).unwrap();
1049+
q.push_back(2).unwrap();
1050+
q.push_back(3).unwrap();
1051+
1052+
// Deque contains: 0, 1, 2, 3
1053+
assert_eq!(q.pop_front(), Some(0));
1054+
assert_eq!(q.pop_front(), Some(1));
1055+
1056+
// Deque contains: ., ., 2, 3
1057+
q.push_back(4).unwrap();
1058+
1059+
// Deque contains: 4, ., 2, 3
1060+
assert_eq!(q.as_slices(), (&[2, 3][..], &[4][..]));
1061+
1062+
assert_eq!(q.make_contiguous(), &[2, 3, 4]);
1063+
1064+
// Deque contains: ., 2, 3, 4
1065+
assert_eq!(q.as_slices(), (&[2, 3, 4][..], &[][..]));
1066+
1067+
assert_eq!(q.pop_front(), Some(2));
1068+
assert_eq!(q.pop_front(), Some(3));
1069+
q.push_back(5).unwrap();
1070+
q.push_back(6).unwrap();
1071+
1072+
// Deque contains: 5, 6, ., 4
1073+
assert_eq!(q.as_slices(), (&[4][..], &[5, 6][..]));
1074+
1075+
assert_eq!(q.make_contiguous(), &[4, 5, 6]);
1076+
1077+
// Deque contains: 4, 5, 6, .
1078+
assert_eq!(q.as_slices(), (&[4, 5, 6][..], &[][..]));
1079+
1080+
assert_eq!(q.pop_front(), Some(4));
1081+
q.push_back(7).unwrap();
1082+
q.push_back(8).unwrap();
1083+
1084+
// Deque contains: 8, 5, 6, 7
1085+
assert_eq!(q.as_slices(), (&[5, 6, 7][..], &[8][..]));
1086+
1087+
assert_eq!(q.make_contiguous(), &[5, 6, 7, 8]);
1088+
1089+
// Deque contains: 5, 6, 7, 8
1090+
assert_eq!(q.as_slices(), (&[5, 6, 7, 8][..], &[][..]));
1091+
}
8691092
}

0 commit comments

Comments
 (0)