Skip to content

Commit

Permalink
Make median3 a safe function
Browse files Browse the repository at this point in the history
  • Loading branch information
Voultapher committed Sep 25, 2023
1 parent 828d9f0 commit 7dc26c5
Showing 1 changed file with 17 additions and 18 deletions.
35 changes: 17 additions & 18 deletions src/quicksort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ where
let v_base = v.as_ptr();
let len = v.len();
let len_div_8 = len / 8;

let a = v_base; // [0, floor(n/8))
let b = v_base.add(len_div_8 * 4); // [4*floor(n/8), 5*floor(n/8))
let c = v_base.add(len_div_8 * 7); // [7*floor(n/8), 8*floor(n/8))

if len < PSEUDO_MEDIAN_REC_THRESHOLD {
median3(a, b, c, is_less).sub_ptr(v_base)
median3(&*a, &*b, &*c, is_less).sub_ptr(v_base)
} else {
median3_rec(a, b, c, len_div_8, is_less).sub_ptr(v_base)
}
Expand Down Expand Up @@ -137,37 +138,35 @@ where
b = median3_rec(b, b.add(n8 * 4), b.add(n8 * 7), n8, is_less);
c = median3_rec(c, c.add(n8 * 4), c.add(n8 * 7), n8, is_less);
}
median3(a, b, c, is_less)
median3(&*a, &*b, &*c, is_less)
}
}

/// Calculates the median of 3 elements.
///
/// SAFETY: a, b, c must be valid initialized elements.
#[inline(always)]
unsafe fn median3<T, F>(a: *const T, b: *const T, c: *const T, is_less: &mut F) -> *const T
fn median3<T, F>(a: &T, b: &T, c: &T, is_less: &mut F) -> *const T
where
F: FnMut(&T, &T) -> bool,
{
// Compiler tends to make this branchless when sensible, and avoids the
// third comparison when not.
unsafe {
let x = is_less(&*a, &*b);
let y = is_less(&*a, &*c);
if x == y {
// If x=y=0 then b, c <= a. In this case we want to return max(b, c).
// If x=y=1 then a < b, c. In this case we want to return min(b, c).
// By toggling the outcome of b < c using XOR x we get this behavior.
let z = is_less(&*b, &*c);
if z ^ x {
c
} else {
b
}
let x = is_less(a, b);
let y = is_less(a, c);
if x == y {
// If x=y=0 then b, c <= a. In this case we want to return max(b, c).
// If x=y=1 then a < b, c. In this case we want to return min(b, c).
// By toggling the outcome of b < c using XOR x we get this behavior.
let z = is_less(b, c);
if z ^ x {
c
} else {
// Either c <= a < b or b <= a < c, thus a is our median.
a
b
}
} else {
// Either c <= a < b or b <= a < c, thus a is our median.
a
}
}

Expand Down

0 comments on commit 7dc26c5

Please sign in to comment.