Skip to content

Commit edadef8

Browse files
authored
Merge pull request #448 from ansg191/make_contiguous
Adds `Deque::make_contiguous`
2 parents b1f040d + 6bc7b3a commit edadef8

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1515
- Added infallible conversions from arrays to `Vec`.
1616
- Added `Vec::spare_capacity_mut`.
1717
- Added `Extend` impls for `Deque`.
18+
- Added `Deque::make_contiguous`.
1819

1920
### Changed
2021

src/deque.rs

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

0 commit comments

Comments
 (0)