Skip to main content

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}