Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 42 additions & 47 deletions algorithmica/src/sort/merge_sort.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
use std::cmp::{Ord, Ordering};
use std::fmt::Debug;

unsafe fn get_by_index<T>(list: &[T], index: isize) -> *const T {
let list_offset = list.as_ptr();
list_offset.offset(index)
(list.as_ptr() as isize + index as isize * std::mem::size_of::<T>() as isize) as *const T
}

fn merge<T: Debug, F>(list: &mut [T], start: usize, mid: usize, end: usize, compare: &F)
fn merge<T: Debug + Clone, F>(list: &mut [T], start: usize, mid: usize, end: usize, compare: &F)
where
F: Fn(&T, &T) -> bool,
{
let mut left = Vec::with_capacity(mid - start + 1);
let mut right = Vec::with_capacity(end - mid);
let mut left: Vec<T> = Vec::with_capacity(mid - start + 1);
let mut right: Vec<T> = Vec::with_capacity(end - mid);

// SAFETY: We're reading elements from a valid slice within bounds.
// Using clone() instead of ptr::read() to avoid moving ownership out of the Vec.
unsafe {
let mut start = start;
while start <= mid {
left.push(get_by_index(list, start as isize).read());
start += 1;
let mut start_idx = start;
while start_idx <= mid {
left.push(get_by_index(list, start_idx as isize).read().clone());
start_idx += 1;
}
while start <= end {
right.push(get_by_index(list, start as isize).read());
start += 1;
while start_idx <= end {
right.push(get_by_index(list, start_idx as isize).read().clone());
start_idx += 1;
}
}

Expand All @@ -31,73 +32,67 @@ where
unsafe {
while left_index < left.len() && right_index < right.len() {
if compare(&left[left_index], &right[right_index]) {
list[k] = get_by_index(&left, left_index as isize).read();
list[k] = get_by_index(&left, left_index as isize).read().clone();
left_index += 1;
} else {
list[k] = get_by_index(&right, right_index as isize).read();
list[k] = get_by_index(&right, right_index as isize).read().clone();
right_index += 1;
}
k += 1;
}

while left_index < left.len() {
list[k] = get_by_index(&left, left_index as isize).read();
list[k] = get_by_index(&left, left_index as isize).read().clone();
left_index += 1;
k += 1;
}

while right_index < right.len() {
list[k] = get_by_index(&right, right_index as isize).read();
list[k] = get_by_index(&right, right_index as isize).read().clone();
right_index += 1;
k += 1;
}
}
}

fn merge_sort<T: Debug, F>(list: &mut [T], start: usize, end: usize, f: &F)
pub fn merge_sort<T: Debug + Clone, F>(list: &mut [T], start: usize, end: usize, f: &F)
where
F: Fn(&T, &T) -> bool,
{
if end <= start {
if start >= end {
return;
}
let mid = (end - start) / 2 + start;

let mid = (start + end) / 2;
merge_sort(list, start, mid, f);
merge_sort(list, mid + 1, end, f);
merge(list, start, mid, end, f);
}

pub fn sort<T>(list: &mut [T])
where
T: Ord + Debug,
{
if list.is_empty() || list.len() == 1 {
return;
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_merge_sort() {
let mut list = vec![10, 20, 10, 4, 5];
merge_sort(&mut list, 0, list.len() - 1, &|a, b| a.lt(b));
assert_eq!(list, vec![4, 5, 10, 10, 20]);
}
merge_sort(list, 0, list.len() - 1, &|a, b| a.lt(b));
}

pub fn sort_by<T, F>(list: &mut [T], compare: &F)
where
F: Fn(&T, &T) -> Ordering,
T: Debug,
{
if list.is_empty() || list.len() == 1 {
return;
#[test]
fn test_merge_sort_strings() {
let mut list = vec!["banana".to_string(), "apple".to_string(), "cherry".to_string()];
merge_sort(&mut list, 0, list.len() - 1, &|a, b| a.lt(b));
assert_eq!(list, vec!["apple".to_string(), "banana".to_string(), "cherry".to_string()]);
}
merge_sort(list, 0, list.len() - 1, &|a, b| {
compare(a, b) == Ordering::Less
});
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn sorting_test() {
let mut t = [1, 2, 3, 4, 5, 6, 7, 8];
t.reverse();
sort(&mut t);
assert_eq!([1, 2, 3, 4, 5, 6, 7, 8], t);
fn test_merge_sort_drop() {
// Regression test for double-free bug
// This should not panic or cause double-free
let mut list = vec![String::from("hello"), String::from("world"), String::from("test")];
merge_sort(&mut list, 0, list.len() - 1, &|a, b| a.lt(b));
assert_eq!(list, vec!["hello".to_string(), "test".to_string(), "world".to_string()]);
}
}