Skip to content

Commit ae57bdf

Browse files
committed
Use last swap optimization in bubblesort
1 parent 2fa330e commit ae57bdf

File tree

1 file changed

+18
-9
lines changed

1 file changed

+18
-9
lines changed

core/src/slice/sort/select.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -317,23 +317,32 @@ fn median_idx<T, F: FnMut(&T, &T) -> bool>(
317317

318318
// It's possible to re-use the insertion sort in the smallsort module, but with optimize_for_size it
319319
// would clutter that module with cfg statements and make it generally harder to read and develop.
320-
// So to decouple things and simplify it, we use a an even smaller bubble sort.
320+
// So to decouple things and simplify it, we use an even smaller bubble sort.
321321
#[cfg(feature = "optimize_for_size")]
322322
fn bubble_sort<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) {
323+
use crate::ptr;
324+
323325
let mut n = v.len();
324-
let mut did_swap = true;
325326

326-
while did_swap && n > 1 {
327-
did_swap = false;
328-
for i in 1..n {
327+
let v_base = v.as_mut_ptr();
328+
329+
while n > 1 {
330+
let loop_n = n;
331+
n = 0;
332+
for i in 1..loop_n {
329333
// SAFETY: The loop construction implies that `i` and `i - 1` will always be in-bounds.
330334
unsafe {
331-
if is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) {
332-
v.swap_unchecked(i - 1, i);
333-
did_swap = true;
335+
// Even if `is_less` erroneously always returns true, we are guaranteed that `n`
336+
// reduces by one each out loop iteration, because `1..n` is exclusive. This
337+
// guarantees a bounded run-time should `Ord` be implemented incorrectly.
338+
let v_i = v_base.add(i);
339+
let v_i_minus_one = v_base.add(i - 1);
340+
341+
if is_less(&*v_i, &*v_i_minus_one) {
342+
ptr::swap_nonoverlapping(v_i, v_i_minus_one, 1);
343+
n = i;
334344
}
335345
}
336346
}
337-
n -= 1;
338347
}
339348
}

0 commit comments

Comments
 (0)