Skip to content

Commit

Permalink
Optimization: stop analyzing the files as soon as there are any diffe…
Browse files Browse the repository at this point in the history
…rences
  • Loading branch information
oSoMoN committed Feb 22, 2024
1 parent 9f64e5b commit ac684c3
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 38 deletions.
60 changes: 49 additions & 11 deletions src/context_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ impl Mismatch {
}

// Produces a diff between the expected output and actual output.
fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec<Mismatch> {
fn make_diff(
expected: &[u8],
actual: &[u8],
context_size: usize,
stop_early: bool,
) -> Vec<Mismatch> {
let mut line_number_expected = 1;
let mut line_number_actual = 1;
let mut context_queue: VecDeque<&[u8]> = VecDeque::with_capacity(context_size);
Expand Down Expand Up @@ -191,6 +196,10 @@ fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec<Mismatc
line_number_actual += 1;
}
}
if stop_early && !results.is_empty() {
// Optimization: stop analyzing the files as soon as there are any differences
return results;
}
}

results.push(mismatch);
Expand Down Expand Up @@ -260,12 +269,15 @@ pub fn diff(
actual: &[u8],
actual_filename: &str,
context_size: usize,
stop_early: bool,
) -> Vec<u8> {
let mut output = format!("*** {expected_filename}\t\n--- {actual_filename}\t\n").into_bytes();
let diff_results = make_diff(expected, actual, context_size);
let diff_results = make_diff(expected, actual, context_size, stop_early);
if diff_results.is_empty() {
return Vec::new();
};
} else if stop_early {
return output;
}
for result in diff_results {
let mut line_number_expected = result.line_number_expected;
let mut line_number_actual = result.line_number_actual;
Expand Down Expand Up @@ -404,8 +416,14 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff =
diff(&alef, "a/alef", &bet, &format!("{target}/alef"), 2);
let diff = diff(
&alef,
"a/alef",
&bet,
&format!("{target}/alef"),
2,
false,
);
File::create(&format!("{target}/ab.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -477,8 +495,14 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff =
diff(&alef, "a/alef_", &bet, &format!("{target}/alef_"), 2);
let diff = diff(
&alef,
"a/alef_",
&bet,
&format!("{target}/alef_"),
2,
false,
);
File::create(&format!("{target}/ab_.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -553,8 +577,14 @@ mod tests {
};
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff =
diff(&alef, "a/alefx", &bet, &format!("{target}/alefx"), 2);
let diff = diff(
&alef,
"a/alefx",
&bet,
&format!("{target}/alefx"),
2,
false,
);
File::create(&format!("{target}/abx.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -632,8 +662,14 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff =
diff(&alef, "a/alefr", &bet, &format!("{target}/alefr"), 2);
let diff = diff(
&alef,
"a/alefr",
&bet,
&format!("{target}/alefr"),
2,
false,
);
File::create(&format!("{target}/abr.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -662,4 +698,6 @@ mod tests {
}
}
}

// TODO: add tests for stop_early parameter
}
18 changes: 14 additions & 4 deletions src/ed_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Mismatch {
}

// Produces a diff between the expected output and actual output.
fn make_diff(expected: &[u8], actual: &[u8]) -> Result<Vec<Mismatch>, DiffError> {
fn make_diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Result<Vec<Mismatch>, DiffError> {
let mut line_number_expected = 1;
let mut line_number_actual = 1;
let mut results = Vec::new();
Expand Down Expand Up @@ -94,6 +94,10 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Result<Vec<Mismatch>, DiffError>
}
}
}
if stop_early && !results.is_empty() {
// Optimization: stop analyzing the files as soon as there are any differences
return Ok(results);
}
}

if !mismatch.actual.is_empty() || !mismatch.expected.is_empty() {
Expand All @@ -103,9 +107,13 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Result<Vec<Mismatch>, DiffError>
Ok(results)
}

pub fn diff(expected: &[u8], actual: &[u8]) -> Result<Vec<u8>, DiffError> {
pub fn diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Result<Vec<u8>, DiffError> {
let mut output = Vec::new();
let diff_results = make_diff(expected, actual)?;
let diff_results = make_diff(expected, actual, stop_early)?;
if stop_early && !diff_results.is_empty() {
write!(&mut output, "\0").unwrap();
return Ok(output);
}
let mut lines_offset = 0;
for result in diff_results {
let line_number_expected: isize = result.line_number_expected as isize + lines_offset;
Expand Down Expand Up @@ -151,7 +159,7 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
pub fn diff_w(expected: &[u8], actual: &[u8], filename: &str) -> Result<Vec<u8>, DiffError> {
let mut output = diff(expected, actual)?;
let mut output = diff(expected, actual, false)?;
writeln!(&mut output, "w {filename}").unwrap();
Ok(output)
}
Expand Down Expand Up @@ -380,4 +388,6 @@ mod tests {
}
}
}

// TODO: add tests for stop_early parameter
}
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,24 @@ fn main() -> ExitCode {
};
// run diff
let result: Vec<u8> = match format {
Format::Normal => normal_diff::diff(&from_content, &to_content),
Format::Normal => normal_diff::diff(&from_content, &to_content, brief),
Format::Unified => unified_diff::diff(
&from_content,
&from.to_string_lossy(),
&to_content,
&to.to_string_lossy(),
context_count,
brief,
),
Format::Context => context_diff::diff(
&from_content,
&from.to_string_lossy(),
&to_content,
&to.to_string_lossy(),
context_count,
brief,
),
Format::Ed => ed_diff::diff(&from_content, &to_content).unwrap_or_else(|error| {
Format::Ed => ed_diff::diff(&from_content, &to_content, brief).unwrap_or_else(|error| {
eprintln!("{error}");
exit(2);
}),
Expand Down
26 changes: 18 additions & 8 deletions src/normal_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl Mismatch {
}

// Produces a diff between the expected output and actual output.
fn make_diff(expected: &[u8], actual: &[u8]) -> Vec<Mismatch> {
fn make_diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Vec<Mismatch> {
let mut line_number_expected = 1;
let mut line_number_actual = 1;
let mut results = Vec::new();
Expand Down Expand Up @@ -100,6 +100,10 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Vec<Mismatch> {
}
}
}
if stop_early && !results.is_empty() {
// Optimization: stop analyzing the files as soon as there are any differences
return results;
}
}

if !mismatch.actual.is_empty() || !mismatch.expected.is_empty() {
Expand All @@ -110,11 +114,15 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Vec<Mismatch> {
}

#[must_use]
pub fn diff(expected: &[u8], actual: &[u8]) -> Vec<u8> {
pub fn diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Vec<u8> {
// See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Normal.html
// for details on the syntax of the normal format.
let mut output = Vec::new();
let diff_results = make_diff(expected, actual);
let diff_results = make_diff(expected, actual, stop_early);
if stop_early && !diff_results.is_empty() {
write!(&mut output, "\0").unwrap();
return output;
}
for result in diff_results {
let line_number_expected = result.line_number_expected;
let line_number_actual = result.line_number_actual;
Expand Down Expand Up @@ -212,7 +220,7 @@ mod tests {
a.write_all(b"a\n").unwrap();
let mut b = Vec::new();
b.write_all(b"b\n").unwrap();
let diff = diff(&a, &b);
let diff = diff(&a, &b, false);
let expected = b"1c1\n< a\n---\n> b\n".to_vec();
assert_eq!(diff, expected);
}
Expand Down Expand Up @@ -265,7 +273,7 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff = diff(&alef, &bet);
let diff = diff(&alef, &bet, false);
File::create(&format!("{target}/ab.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -357,7 +365,7 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff = diff(&alef, &bet);
let diff = diff(&alef, &bet, false);
File::create(&format!("{target}/abn.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -431,7 +439,7 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff = diff(&alef, &bet);
let diff = diff(&alef, &bet, false);
File::create(&format!("{target}/ab_.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -509,7 +517,7 @@ mod tests {
}
// This test diff is intentionally reversed.
// We want it to turn the alef into bet.
let diff = diff(&alef, &bet);
let diff = diff(&alef, &bet, false);
File::create(&format!("{target}/abr.diff"))
.unwrap()
.write_all(&diff)
Expand Down Expand Up @@ -538,4 +546,6 @@ mod tests {
}
}
}

// TODO: add tests for stop_early parameter
}
Loading

0 comments on commit ac684c3

Please sign in to comment.