diff --git a/algorithmica/src/sort/merge_sort.rs b/algorithmica/src/sort/merge_sort.rs index 90e5af0..c7225a6 100644 --- a/algorithmica/src/sort/merge_sort.rs +++ b/algorithmica/src/sort/merge_sort.rs @@ -1,26 +1,27 @@ -use std::cmp::{Ord, Ordering}; use std::fmt::Debug; unsafe fn get_by_index(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::() as isize) as *const T } -fn merge(list: &mut [T], start: usize, mid: usize, end: usize, compare: &F) +fn merge(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 = Vec::with_capacity(mid - start + 1); + let mut right: Vec = 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; } } @@ -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(list: &mut [T], start: usize, end: usize, f: &F) +pub fn merge_sort(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(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(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()]); } }