a/sort/quick.rs
1use core::mem::{self, ManuallyDrop};
2use core::ptr;
3
4use super::heap;
5
6/// Sorts a slice in place using quick sort.
7///
8/// Quick sort picks a pivot element, partitions the slice into the elements smaller than the
9/// pivot and the rest, puts the pivot between them, and sorts the two parts the same way. With
10/// pivots near the median every level halves the parts, so the sort makes `O(n log n)`
11/// comparisons, and it needs no buffer.
12///
13/// Seven refinements keep the algorithm as cheap as quick sort can be:
14///
15/// * **Sorted and reverse-sorted input is recognised.** The slice is first scanned for one
16/// ascending or strictly descending run; if it covers the whole slice, the slice is left
17/// alone or reversed. This costs `n − 1` comparisons on such input and usually two or three
18/// on any other.
19/// * **Pivots from a median of samples.** The pivot is the median of three elements spread over
20/// the slice, from 64 elements on the median of three such medians (the "ninther"), and from
21/// 4096 elements on a pseudo-median of 27 evenly spaced samples, three times as many for
22/// every 16 times the length (243 for a million elements). On random input this keeps
23/// comparisons at about `n·log₂ n`, against `1.39·n·log₂ n` for a random pivot.
24/// * **Branchless partitioning for elements of at most 64 bytes.** Whether an element is
25/// smaller than the pivot is a coin flip on random input, which defeats the branch predictor.
26/// Instead, every element is moved unconditionally, with the comparison only choosing where:
27/// a vacant slot cycles through the slice, and each element either joins the smaller part or
28/// moves to the end of the larger one (Lomuto's scheme, made branchless). That costs two
29/// moves per element, which larger elements pay for more than a misprediction, so they are
30/// partitioned by Hoare's scheme, which moves only misplaced pairs, two moves per pair by
31/// cycling them through a vacant slot instead of three by swapping. On random input the
32/// branchless scheme is 1.9–4.3 times faster than Hoare's for integers and 1.4–2.5 times for
33/// strings.
34/// * **Runs of equal elements in one pass.** If the pivot equals the pivot of the enclosing
35/// partition, which is no larger than any element of this part, the part is partitioned into
36/// elements not larger than the pivot, all equal to it and done, and the rest. Input with `k`
37/// distinct keys costs `O(n log k)`, and all-equal input is never quadratic.
38/// * **Recursion into the smaller part only.** The larger part is sorted by looping, so the
39/// stack never holds more than `log₂ n` frames.
40/// * **Sorting networks for short parts.** For elements of at most 16 bytes, parts of at most
41/// 8 elements are sorted by optimal sorting networks (fixed sequences of 1 to 19
42/// compare-exchanges) made of branchless swaps, unrolled for each length so the elements
43/// stay in registers. On random input this makes integers sort 1.3–1.85 times faster than
44/// with insertion sort. Larger elements pay for a compare-exchange's three moves, so their
45/// parts of at most 16 elements are sorted by insertion sort.
46/// * **A guaranteed `O(n log n)`.** A part still being partitioned after `2·log₂ n` levels
47/// means the pivots were consistently bad, which no sampling rules out (an adversary that
48/// answers comparisons adaptively can force it). Such a part is finished by this crate's
49/// [`heap`](fn@heap) sort, so no input makes the sort quadratic (the "introsort" idea).
50///
51/// # Properties
52///
53/// * **Unstable**: equal elements may be reordered.
54/// * **In place**: no allocation, `O(log n)` stack.
55/// * **Adaptive** to sorted and reverse-sorted input, and to repeated keys.
56///
57/// | Case | Comparisons | Moves |
58/// |-----------------------------------|---------------------------------|--------------------|
59/// | Best (sorted, reverse, all equal) | `n − 1` | `0` (`n` reversed) |
60/// | Average (random) | `≈ n·log₂ n + (0.2–1)·n` | `≈ 2n·log₂(n/8)` |
61/// | `k` distinct keys | `O(n log k)` | `O(n log k)` |
62/// | Worst | `O(n log n)` | `O(n log n)` |
63///
64/// For `n = 1000`: 999 comparisons on sorted or reverse-sorted input and about 10,200 on
65/// random input (`n·log₂ n + 0.22n`); for `n = 100,000` and `n = 1,000,000`, about
66/// `n·log₂ n + 0.6n`. Elements larger than 16 bytes, whose short parts are sorted by insertion
67/// sort, take up to `0.4n` more.
68///
69/// On nearly sorted input (a few elements out of place), Hoare's scheme would be 1.5–1.75 times
70/// faster for integers and 1.2–1.5 times for strings, since its branches are then predictable;
71/// on random input it is 1.9–4.3 times slower. [`slice::sort_unstable`], which shares several
72/// of these ideas, is 1.1–1.2 times faster on random integers from 100 elements on (its short
73/// parts are merged through a buffer, which is only sound for types without interior
74/// mutability, something stable Rust cannot test for). This sort is 1.9 times faster on 20
75/// integers, 1.03–1.14 times faster on random strings and as fast or faster with 256-byte
76/// elements.
77///
78/// # Panics
79///
80/// Does not panic by itself. If the [`Ord`] implementation of `T` panics, the panic is
81/// propagated and `arr` is left in an unspecified order, but it still holds every original
82/// element exactly once: nothing is dropped twice or leaked. An [`Ord`] that is not a total
83/// order leaves `arr` in an unspecified order with every element exactly once as well.
84///
85/// # Examples
86///
87/// ```
88/// use a::sort::quick;
89///
90/// let mut v = [5, 1, 4, 2, 8, 0, 3, 9, 7, 6];
91/// quick(&mut v);
92/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
93///
94/// // Many duplicates are handled in one pass per distinct pivot.
95/// let mut v: Vec<u8> = (0..1000).map(|i| (i * 7 % 3) as u8).collect();
96/// quick(&mut v);
97/// assert!(v.windows(2).all(|w| w[0] <= w[1]));
98///
99/// let mut words = vec!["pear", "apple", "fig"];
100/// quick(&mut words);
101/// assert_eq!(words, ["apple", "fig", "pear"]);
102/// ```
103pub fn quick<T: Ord>(arr: &mut [T]) {
104 let len = arr.len();
105 if len < 2 {
106 return;
107 }
108 // An ascending run stops at the first descent; a strictly descending one at the first
109 // element that is not smaller, so reversing it is all it takes.
110 let descending = arr[1] < arr[0];
111 let run = 2 + if descending {
112 arr[1..].windows(2).take_while(|w| w[1] < w[0]).count()
113 } else {
114 arr[1..].windows(2).take_while(|w| w[1] >= w[0]).count()
115 };
116 if run == len {
117 if descending {
118 arr.reverse();
119 }
120 return;
121 }
122 sort(arr, None, 2 * (len | 1).ilog2());
123}
124
125/// Parts of at most this many elements are sorted by [`sort_short`] instead of partitioned,
126/// when that uses sorting networks.
127const SHORT: usize = 8;
128
129/// Parts of at most this many elements are sorted by [`sort_short`] instead of partitioned,
130/// when that uses insertion sort.
131///
132/// Measured against 8 and 12: 1.02–1.03 times faster on random input for elements of 32 to
133/// 256 bytes, and up to 1.25 times on nearly sorted input; 1.03 times slower for random
134/// `String`s.
135const SHORT_INSERTION: usize = 16;
136
137/// From this length on, the pivot is the median of three medians of three.
138const NINTHER: usize = 64;
139
140/// From this length on, the pivot is the pseudo-median of 27 samples, and of three times as
141/// many for every 16 times the length.
142///
143/// Measured against the ninther: 1.02–1.03 times faster on random input from 100,000
144/// elements, 1.04 times with about `√n` distinct keys and 1.15 times on 1,000,000 elements of
145/// a sawtooth pattern; no change on nearly sorted input or below this length.
146const PSEUDO_MEDIAN: usize = 4096;
147
148/// The largest element size, in bytes, partitioned by [`partition_lomuto`]; larger elements
149/// use [`partition_hoare`].
150///
151/// Measured: Lomuto's branchless scheme is 1.05–1.35 times faster on random input with
152/// 64-byte elements. With 80 and 96 bytes it is still up to 1.15 times faster on random input
153/// of 10,000 elements or more, but 1.1 times slower below that, 1.15–1.25 times slower with
154/// few distinct keys, and 3–4 times slower on nearly sorted input, whose branches Hoare's
155/// scheme predicts.
156const LOMUTO_MAX_SIZE: usize = 64;
157
158/// The largest element size, in bytes, for which short parts are sorted by sorting networks.
159///
160/// Measured on random input: integers, `u128`, pairs and `&str` sort 1.1–1.85 times faster
161/// with networks. From 24 bytes on, the three moves per compare-exchange cost more than
162/// insertion sort's mispredicted branches: `[u64; 3]` and `[u64; 4]` sort 1.1–1.3 times
163/// faster by insertion. `String` is a toss-up: networks are 1.05–1.13 times faster on random
164/// input, insertion sort 1.06–1.2 times faster on nearly sorted input or few distinct keys.
165const NETWORK_MAX_SIZE: usize = 16;
166
167/// Sorts `v`, all of whose elements are not smaller than `ancestor` if there is one, falling
168/// back to heap sort once `limit` more levels of partitioning have not finished the job.
169fn sort<'a, T: Ord>(mut v: &'a mut [T], mut ancestor: Option<&'a T>, mut limit: u32) {
170 loop {
171 let short = if mem::size_of::<T>() <= NETWORK_MAX_SIZE {
172 SHORT
173 } else {
174 SHORT_INSERTION
175 };
176 if v.len() <= short {
177 sort_short(v);
178 return;
179 }
180 if limit == 0 {
181 heap(v);
182 return;
183 }
184 limit -= 1;
185
186 let pivot = choose_pivot(v);
187 v.swap(0, pivot);
188 if let Some(ancestor) = ancestor {
189 // No element is smaller than `ancestor`, so a pivot not greater than it equals it,
190 // and so does every element not greater than the pivot: those are done.
191 if v[0] <= *ancestor {
192 let equal = partition(v, &mut |e: &T, pivot: &T| e <= pivot);
193 v = &mut v[equal + 1..];
194 continue;
195 }
196 }
197 let smaller = partition(v, &mut |e: &T, pivot: &T| e < pivot);
198 v.swap(0, smaller);
199 let (left, rest) = v.split_at_mut(smaller);
200 let (pivot, right) = rest.split_first_mut().expect("the pivot is in `rest`");
201 // The elements of `right` are not smaller than `pivot`, those of `left` not smaller
202 // than `ancestor`.
203 if left.len() < right.len() {
204 sort(left, ancestor, limit);
205 v = right;
206 ancestor = Some(pivot);
207 } else {
208 sort(right, Some(pivot), limit);
209 v = left;
210 }
211 }
212}
213
214/// Partitions `v[1..]` around the pivot `v[0]`, which stays in place: the elements for which
215/// `goes_left(element, pivot)` holds come first. Returns how many there are.
216fn partition<T: Ord>(v: &mut [T], goes_left: &mut impl FnMut(&T, &T) -> bool) -> usize {
217 let (pivot, rest) = v.split_first_mut().expect("`v` is not empty");
218 if mem::size_of::<T>() <= LOMUTO_MAX_SIZE {
219 partition_lomuto(rest, pivot, goes_left)
220 } else {
221 partition_hoare(rest, pivot, goes_left)
222 }
223}
224
225/// Partitions `v` around `pivot` without branching on the comparisons; see [`partition`].
226///
227/// The first element is held out, leaving a vacant slot. Each following element `e` is then
228/// compared with the pivot, the first element of the larger part moves into the vacant slot,
229/// which is at the end of the larger part, `e` moves into the slot that frees up, and `e`'s own
230/// slot becomes the vacant one; `e` joins the smaller part if it goes left, and is the new
231/// first element of the larger part otherwise. Every step makes the same two moves whatever
232/// the comparison says, and the held element is placed the same way at the end.
233fn partition_lomuto<T>(
234 v: &mut [T],
235 pivot: &T,
236 goes_left: &mut impl FnMut(&T, &T) -> bool,
237) -> usize {
238 let len = v.len();
239 if len == 0 {
240 return 0;
241 }
242 let base = v.as_mut_ptr();
243 // SAFETY: every index is below `len`: before step `i` the vacant slot is `i - 1` and
244 // `left <= i - 1`, so all pointers stay inside `v`. Before step `i`, the elements that were
245 // at `1..i` occupy `0..i - 1`, the vacant slot is `i - 1`, and `v[i..]` is untouched; the
246 // comparison runs before the step's moves and with `v[i]` in place, and nothing else can
247 // panic. If it panics, `hole` writes the held element into the vacant slot, so `v` holds
248 // every element exactly once whatever `goes_left` answered before. `pivot` is not in `v`.
249 unsafe {
250 let held = ManuallyDrop::new(ptr::read(base));
251 let mut hole = Hole {
252 src: &*held,
253 dst: base,
254 };
255 let mut left = 0;
256 for i in 1..len {
257 let e = base.add(i);
258 let goes = goes_left(&*e, pivot);
259 let first_larger = base.add(left);
260 ptr::copy(first_larger, hole.dst, 1);
261 ptr::copy_nonoverlapping(e, first_larger, 1);
262 hole.dst = e;
263 left += usize::from(goes);
264 }
265 let goes = goes_left(&*held, pivot);
266 let first_larger = base.add(left);
267 ptr::copy(first_larger, hole.dst, 1);
268 hole.dst = first_larger;
269 // Dropping `hole` writes the held element into the slot just freed.
270 drop(hole);
271 left + usize::from(goes)
272 }
273}
274
275/// Partitions `v` around `pivot` by moving misplaced pairs; see [`partition`].
276///
277/// Scans from the left for an element that does not go left and from the right for one that
278/// does, and repeats until the scans meet. Instead of swapping each such pair, which takes
279/// three moves, the elements are moved in one cycle of two moves per pair: the left element
280/// of the first pair is held out and the right one moves into its slot, leaving a vacant slot
281/// on the right; each later pair moves its left element into the vacant slot and its right
282/// one into the left one's slot, which leaves the right one's slot vacant; and the held
283/// element fills the last vacant slot. With 256-byte elements this is 1.1 times faster.
284fn partition_hoare<T>(v: &mut [T], pivot: &T, goes_left: &mut impl FnMut(&T, &T) -> bool) -> usize {
285 let base = v.as_mut_ptr();
286 let (mut left, mut right) = (0, v.len());
287 // SAFETY: the scans only read `v[left..right]`, and a pair has `left < right`, so every
288 // index stays inside `v` and the two elements of a pair are distinct, whatever
289 // `goes_left` answers. The vacant slot is always at `right` or beyond, outside the part
290 // the scans read, and every element is compared in place, before it moves. If a
291 // comparison panics, `hole` writes the held element into the vacant slot, so `v` holds
292 // every element exactly once. `pivot` is not in `v`.
293 unsafe {
294 // Moves `left` and `right` to the next misplaced pair; returns `false` once the scans
295 // have met instead, with `left == right`. The right scan stops short of `left`, so an
296 // `Ord` that answers differently for the same element cannot make a pair of one.
297 let mut next_pair = |left: &mut usize, right: &mut usize| {
298 while *left < *right && goes_left(&*base.add(*left), pivot) {
299 *left += 1;
300 }
301 if *left == *right {
302 return false;
303 }
304 loop {
305 *right -= 1;
306 if *right == *left {
307 return false;
308 }
309 if goes_left(&*base.add(*right), pivot) {
310 return true;
311 }
312 }
313 };
314 if !next_pair(&mut left, &mut right) {
315 return left;
316 }
317 let held = ManuallyDrop::new(ptr::read(base.add(left)));
318 ptr::copy_nonoverlapping(base.add(right), base.add(left), 1);
319 let mut hole = Hole {
320 src: &*held,
321 dst: base.add(right),
322 };
323 left += 1;
324 while next_pair(&mut left, &mut right) {
325 ptr::copy_nonoverlapping(base.add(left), hole.dst, 1);
326 ptr::copy_nonoverlapping(base.add(right), base.add(left), 1);
327 hole.dst = base.add(right);
328 left += 1;
329 }
330 // Dropping `hole` writes the held element into the last vacant slot.
331 left
332 }
333}
334
335/// Returns the index of the median of three samples of `v`, from [`NINTHER`] elements on of
336/// the median of the medians of three groups of three, and from [`PSEUDO_MEDIAN`] elements on
337/// of a pseudo-median of more samples.
338fn choose_pivot<T: Ord>(v: &[T]) -> usize {
339 let len = v.len();
340 let (a, b, c) = (len / 4, len / 2, len / 4 * 3);
341 if len < NINTHER {
342 return median_of_3(v, a, b, c);
343 }
344 if len < PSEUDO_MEDIAN {
345 let step = len / 8;
346 let a = median_of_3(v, a - step, a, a + step);
347 let b = median_of_3(v, b - step, b, b + step);
348 let c = median_of_3(v, c - step, c, c + step);
349 return median_of_3(v, a, b, c);
350 }
351 let mut depth = 3;
352 let mut reach = PSEUDO_MEDIAN * 16;
353 while len >= reach {
354 depth += 1;
355 reach = reach.saturating_mul(16);
356 }
357 pseudo_median(v, len / 2, len / 3, depth)
358}
359
360/// Returns the index of the median of the pseudo-medians around `mid - step`, `mid` and
361/// `mid + step`, each `depth - 1` levels deep with a third of the step, or at depth 1 of the
362/// three elements there.
363///
364/// The `3^depth` samples are evenly spaced, `step / 3^(depth - 1)` apart, and lie less than
365/// `1.5·step` from `mid`. Samples placed symmetrically around the middle matter: on nearly
366/// sorted input, the standard library's `0`, `n/2` and `7n/8` make the pivot's rank about
367/// `0.56n` and cost 1.08 times as many comparisons.
368fn pseudo_median<T: Ord>(v: &[T], mid: usize, step: usize, depth: u32) -> usize {
369 if depth == 1 {
370 return median_of_3(v, mid - step, mid, mid + step);
371 }
372 let a = pseudo_median(v, mid - step, step / 3, depth - 1);
373 let b = pseudo_median(v, mid, step / 3, depth - 1);
374 let c = pseudo_median(v, mid + step, step / 3, depth - 1);
375 median_of_3(v, a, b, c)
376}
377
378/// Returns whichever of `a`, `b` and `c` indexes the median of the three elements, with two or
379/// three comparisons.
380fn median_of_3<T: Ord>(v: &[T], a: usize, b: usize, c: usize) -> usize {
381 let b_below_a = v[b] < v[a];
382 let c_below_b = v[c] < v[b];
383 if b_below_a == c_below_b {
384 // `a <= b <= c` or `a > b > c`.
385 return b;
386 }
387 // `b` is the smallest or the largest: the median is the other extreme of `a` and `c`.
388 let c_below_a = v[c] < v[a];
389 if b_below_a == c_below_a { c } else { a }
390}
391
392/// Sorts a part of at most [`SHORT`] elements by a sorting network, or for elements larger
393/// than [`NETWORK_MAX_SIZE`] a part of at most [`SHORT_INSERTION`] elements by insertion sort.
394fn sort_short<T: Ord>(v: &mut [T]) {
395 if mem::size_of::<T>() <= NETWORK_MAX_SIZE {
396 match v.len() {
397 0 | 1 => {}
398 2 => network::<T, 2>(v),
399 3 => network::<T, 3>(v),
400 4 => network::<T, 4>(v),
401 5 => network::<T, 5>(v),
402 6 => network::<T, 6>(v),
403 7 => network::<T, 7>(v),
404 8 => network::<T, 8>(v),
405 _ => unreachable!("parts of more than {SHORT} elements are partitioned"),
406 }
407 } else {
408 for end in 2..=v.len() {
409 insert_last(&mut v[..end]);
410 }
411 }
412}
413
414/// Sorts `v`, which has `N` elements, by the sorting network for `N` elements.
415///
416/// Each length gets its own copy of the network, with constant indices, so the compiler keeps
417/// the elements in registers between compare-exchanges. A loop over the table with the length
418/// known only at run time reloads both elements and checks both indices for each of them;
419/// that made the whole sort of random integers 1.07 times slower from 100,000 elements, 1.2
420/// times with 100 and 1.5 times with 20.
421#[inline(always)]
422fn network<T: Ord, const N: usize>(v: &mut [T]) {
423 let v: &mut [T; N] = v.try_into().expect("`v` has `N` elements");
424 for &(i, j) in NETWORKS[N] {
425 compare_exchange(v, i, j);
426 }
427}
428
429/// Moves the last element of `v` left past the elements greater than it, `v[..len - 1]`
430/// being sorted.
431///
432/// A plain linear insertion: for parts this short, it is 1.08–1.18 times faster than this
433/// crate's [`insertion`](fn@super::insertion) sort, which inserts elements in pairs with a
434/// galloping search, for 64-byte elements, and 1.16 times faster for 256-byte ones.
435fn insert_last<T: Ord>(v: &mut [T]) {
436 let base = v.as_mut_ptr();
437 let last = v.len() - 1;
438 // SAFETY: `last >= 1`, since `sort_short` passes at least two elements, and the hole only
439 // moves left while it is above `base`, so every pointer stays inside `v`. The last element
440 // is compared in place, then read out, which leaves its slot vacant; each step moves the
441 // element left of the vacant slot into it, so the vacant slot moves one to the left. The
442 // held element is compared as the copy that gets written back, and if a comparison
443 // panics, `hole` writes it into the vacant slot, so `v` holds every element exactly once.
444 unsafe {
445 if *base.add(last) >= *base.add(last - 1) {
446 return;
447 }
448 let held = ManuallyDrop::new(ptr::read(base.add(last)));
449 let mut hole = Hole {
450 src: &*held,
451 dst: base.add(last),
452 };
453 loop {
454 ptr::copy_nonoverlapping(hole.dst.sub(1), hole.dst, 1);
455 hole.dst = hole.dst.sub(1);
456 if hole.dst == base || *held >= *hole.dst.sub(1) {
457 break;
458 }
459 }
460 // Dropping `hole` writes the held element into the vacant slot.
461 }
462}
463
464/// Optimal sorting networks for 0 to 8 elements: applying [`compare_exchange`] to each pair in
465/// order sorts any input of that length (1, 3, 5, 9, 12, 16 and 19 comparisons from 2 on).
466const NETWORKS: [&[(usize, usize)]; SHORT + 1] = [
467 &[],
468 &[],
469 &[(0, 1)],
470 &[(0, 2), (0, 1), (1, 2)],
471 &[(0, 2), (1, 3), (0, 1), (2, 3), (1, 2)],
472 &[
473 (0, 3),
474 (1, 4),
475 (0, 2),
476 (1, 3),
477 (0, 1),
478 (2, 4),
479 (1, 2),
480 (3, 4),
481 (2, 3),
482 ],
483 &[
484 (0, 5),
485 (1, 3),
486 (2, 4),
487 (1, 2),
488 (3, 4),
489 (0, 3),
490 (2, 5),
491 (0, 1),
492 (2, 3),
493 (4, 5),
494 (1, 2),
495 (3, 4),
496 ],
497 &[
498 (0, 6),
499 (2, 3),
500 (4, 5),
501 (0, 2),
502 (1, 4),
503 (3, 6),
504 (0, 1),
505 (2, 5),
506 (3, 4),
507 (1, 2),
508 (4, 6),
509 (2, 3),
510 (4, 5),
511 (1, 2),
512 (3, 4),
513 (5, 6),
514 ],
515 &[
516 (0, 2),
517 (1, 3),
518 (4, 6),
519 (5, 7),
520 (0, 4),
521 (1, 5),
522 (2, 6),
523 (3, 7),
524 (0, 1),
525 (2, 3),
526 (4, 5),
527 (6, 7),
528 (2, 4),
529 (3, 5),
530 (1, 4),
531 (3, 6),
532 (1, 2),
533 (3, 4),
534 (5, 6),
535 ],
536];
537
538/// Swaps `v[i]` and `v[j]`, `i < j`, if `v[j] < v[i]`, without branching on the comparison.
539#[inline(always)]
540fn compare_exchange<T: Ord>(v: &mut [T], i: usize, j: usize) {
541 assert!(i < j && j < v.len());
542 let base = v.as_mut_ptr();
543 // SAFETY: `i` and `j` are distinct indices into `v`. The comparison runs with both
544 // elements in place, so a panic leaves `v` untouched. Then the element that belongs at `j`
545 // is read out, the one that belongs at `i` is copied there (onto itself if no swap is
546 // needed, which `ptr::copy` allows), and the element read out is written to `j`. Nothing in
547 // between can panic.
548 unsafe {
549 let (a, b) = (base.add(i), base.add(j));
550 let swap = *b < *a;
551 let (to_a, to_b) = if swap { (b, a) } else { (a, b) };
552 let tmp = ManuallyDrop::new(ptr::read(to_b));
553 ptr::copy(to_a, a, 1);
554 ptr::copy_nonoverlapping(&*tmp, b, 1);
555 }
556}
557
558/// A vacant slot of a slice, plus the element held outside the slice that belongs there.
559///
560/// Writing the held element on drop is what keeps [`partition_lomuto`], [`partition_hoare`]
561/// and [`insert_last`] panic-safe: the slice is whole again even if a comparison unwinds while
562/// the element is out.
563struct Hole<T> {
564 src: *const T,
565 dst: *mut T,
566}
567
568impl<T> Drop for Hole<T> {
569 fn drop(&mut self) {
570 // SAFETY: `dst` points to the single vacant slot of the slice being partitioned, and
571 // `src` to the held element, which is never used again after this write.
572 unsafe { ptr::copy_nonoverlapping(self.src, self.dst, 1) }
573 }
574}