From e0ebf907a4fec88d81bb20b226f0ade6ae932d60 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 8 May 2021 23:06:17 +0200 Subject: [PATCH] sort: make merging stable When merging files we need to prioritize files that occur earlier in the command line arguments with -m. This also makes the extsort merge step (and thus extsort itself) stable again. --- src/uu/sort/src/sort.rs | 11 ++++++++++- tests/by-util/test_sort.rs | 24 +++++++++++++++++++++++ tests/fixtures/sort/ext_stable.expected | 4 ++++ tests/fixtures/sort/ext_stable.txt | 4 ++++ tests/fixtures/sort/merge_stable.expected | 3 +++ tests/fixtures/sort/merge_stable_1.txt | 2 ++ tests/fixtures/sort/merge_stable_2.txt | 1 + 7 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/sort/ext_stable.expected create mode 100644 tests/fixtures/sort/ext_stable.txt create mode 100644 tests/fixtures/sort/merge_stable.expected create mode 100644 tests/fixtures/sort/merge_stable_1.txt create mode 100644 tests/fixtures/sort/merge_stable_2.txt diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 730be0039..d35c62f87 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -686,6 +686,7 @@ struct MergeableFile<'a> { lines: Box + 'a>, current_line: Line, settings: &'a GlobalSettings, + file_index: usize, } // BinaryHeap depends on `Ord`. Note that we want to pop smallest items @@ -693,7 +694,14 @@ struct MergeableFile<'a> { // trick it into the right order by calling reverse() here. impl<'a> Ord for MergeableFile<'a> { fn cmp(&self, other: &MergeableFile) -> Ordering { - compare_by(&self.current_line, &other.current_line, self.settings).reverse() + let comparison = compare_by(&self.current_line, &other.current_line, self.settings); + if comparison == Ordering::Equal { + // If lines are equal, the earlier file takes precedence. + self.file_index.cmp(&other.file_index) + } else { + comparison + } + .reverse() } } @@ -729,6 +737,7 @@ impl<'a> FileMerger<'a> { lines, current_line: next_line, settings: &self.settings, + file_index: self.heap.len(), }; self.heap.push(mergeable_file); } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 4465e861f..bad9d577e 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -51,6 +51,18 @@ fn test_smaller_than_specified_segment() { .stdout_is_fixture("ext_sort.expected"); } +#[test] +fn test_ext_sort_stable() { + new_ucmd!() + .arg("-n") + .arg("--stable") + .arg("-S") + .arg("0M") + .arg("ext_stable.txt") + .succeeds() + .stdout_only_fixture("ext_stable.expected"); +} + #[test] fn test_extsort_zero_terminated() { new_ucmd!() @@ -566,6 +578,18 @@ fn test_merge_unique() { .stdout_only_fixture("merge_ints_interleaved.expected"); } +#[test] +fn test_merge_stable() { + new_ucmd!() + .arg("-m") + .arg("--stable") + .arg("-n") + .arg("merge_stable_1.txt") + .arg("merge_stable_2.txt") + .succeeds() + .stdout_only_fixture("merge_stable.expected"); +} + #[test] fn test_merge_reversed() { new_ucmd!() diff --git a/tests/fixtures/sort/ext_stable.expected b/tests/fixtures/sort/ext_stable.expected new file mode 100644 index 000000000..11ca4deb7 --- /dev/null +++ b/tests/fixtures/sort/ext_stable.expected @@ -0,0 +1,4 @@ +0a +0a +0b +0b diff --git a/tests/fixtures/sort/ext_stable.txt b/tests/fixtures/sort/ext_stable.txt new file mode 100644 index 000000000..11ca4deb7 --- /dev/null +++ b/tests/fixtures/sort/ext_stable.txt @@ -0,0 +1,4 @@ +0a +0a +0b +0b diff --git a/tests/fixtures/sort/merge_stable.expected b/tests/fixtures/sort/merge_stable.expected new file mode 100644 index 000000000..49f57888d --- /dev/null +++ b/tests/fixtures/sort/merge_stable.expected @@ -0,0 +1,3 @@ +0a +0c +0b diff --git a/tests/fixtures/sort/merge_stable_1.txt b/tests/fixtures/sort/merge_stable_1.txt new file mode 100644 index 000000000..20528104f --- /dev/null +++ b/tests/fixtures/sort/merge_stable_1.txt @@ -0,0 +1,2 @@ +0a +0c \ No newline at end of file diff --git a/tests/fixtures/sort/merge_stable_2.txt b/tests/fixtures/sort/merge_stable_2.txt new file mode 100644 index 000000000..d3523d976 --- /dev/null +++ b/tests/fixtures/sort/merge_stable_2.txt @@ -0,0 +1 @@ +0b \ No newline at end of file