// Modified for gitoxide from the upstream imara-diff crate.// Upstream source: git cat-file -p 32d1e45d3df061e6ccba6db7fdce92db29e345d8:src/histogram/lcs.rs use crate::histogram::{Histogram, MAX_CHAIN_LEN};use crate::intern::Token; /// Finds the longest common subsequence (LCS) using a histogram-based approach.////// Returns `None` if the sequences are highly repetitive and should fall back to Myers.pub(super) fn find_lcs(before: &[Token], after: &[Token], histogram: &mut Histogram) -> Option<Lcs> { let mut search = LcsSearch { lcs: Lcs::default(), min_occurrences: MAX_CHAIN_LEN + 1, found_cs: false, }; search.run(before, after, histogram); if search.success() { Some(search.lcs) } else { None }} /// Represents a longest common subsequence found by the histogram algorithm.#[derive(Default, Debug)]pub struct Lcs { /// Starting position in the "before" sequence. pub before_start: u32, /// Starting position in the "after" sequence. pub after_start: u32, /// Length of the common subsequence. pub len: u32,} /// State for searching for the longest common subsequence.pub struct LcsSearch { /// The best LCS found so far. lcs: Lcs, /// The minimum occurrence count of tokens in the best LCS. min_occurrences: u32, /// Whether any common subsequence was found. found_cs: bool,} impl LcsSearch { fn run(&mut self, before: &[Token], after: &[Token], histogram: &mut Histogram) { let mut pos = 0; while let Some(&token) = after.get(pos as usize) { if histogram.num_token_occurrences(token) != 0 { self.found_cs = true; if histogram.num_token_occurrences(token) <= self.min_occurrences { pos = self.update_lcs(pos, token, histogram, before, after); continue; } } pos += 1; } histogram.clear(); } fn success(&mut self) -> bool { !self.found_cs || self.min_occurrences <= MAX_CHAIN_LEN } fn update_lcs( &mut self, after_pos: u32, token: Token, histogram: &Histogram, before: &[Token], after: &[Token], ) -> u32 { let mut next_token_idx2 = after_pos + 1; let mut occurrences_iter = histogram.token_occurrences(token).iter().copied(); let mut token_idx1 = occurrences_iter.next().unwrap(); 'occurrences_iter: loop { let mut occurrences = histogram.num_token_occurrences(token); let mut start1 = token_idx1; let mut start2 = after_pos; loop { if start1 == 0 || start2 == 0 { break; } let token1 = before.get(start1 as usize - 1); let token2 = after.get(start2 as usize - 1); if matches!((token1, token2), (Some(token1), Some(token2)) if token1 == token2) { start1 -= 1; start2 -= 1; let new_occurrences = histogram.num_token_occurrences(before[start1 as usize]); occurrences = occurrences.min(new_occurrences); } else { break; } } let mut end1 = token_idx1 + 1; let mut end2 = after_pos + 1; loop { let token1 = before.get(end1 as usize); let token2 = after.get(end2 as usize); if matches!((token1, token2), (Some(token1), Some(token2)) if token1 == token2) { let new_occurrences = histogram.num_token_occurrences(before[end1 as usize]); occurrences = occurrences.min(new_occurrences); end1 += 1; end2 += 1; } else { break; } } if next_token_idx2 < end2 { next_token_idx2 = end2; } let len = end2 - start2; debug_assert_eq!(len, end1 - start1); if self.lcs.len < len || self.min_occurrences > occurrences { self.min_occurrences = occurrences; self.lcs = Lcs { before_start: start1, after_start: start2, len, }; } loop { if let Some(next_token_idx) = occurrences_iter.next() { if next_token_idx > end2 { token_idx1 = next_token_idx; break; } } else { break 'occurrences_iter; } } } next_token_idx2 }}
// Modified for gitoxide from the upstream imara-diff crate.
// Upstream source: git cat-file -p 32d1e45d3df061e6ccba6db7fdce92db29e345d8:src/histogram/lcs.rs
use crate::histogram::{Histogram, MAX_CHAIN_LEN};
use crate::intern::Token;
/// Finds the longest common subsequence (LCS) using a histogram-based approach.
///
/// Returns `None` if the sequences are highly repetitive and should fall back to Myers.
pub(super) fn find_lcs(before: &[Token], after: &[Token], histogram: &mut Histogram) -> Option<Lcs> {
let mut search = LcsSearch {
lcs: Lcs::default(),
min_occurrences: MAX_CHAIN_LEN + 1,
found_cs: false,
};
search.run(before, after, histogram);
if search.success() { Some(search.lcs) } else { None }
}
/// Represents a longest common subsequence found by the histogram algorithm.
#[derive(Default, Debug)]
pub struct Lcs {
/// Starting position in the "before" sequence.
pub before_start: u32,
/// Starting position in the "after" sequence.
pub after_start: u32,
/// Length of the common subsequence.
pub len: u32,
}
/// State for searching for the longest common subsequence.
pub struct LcsSearch {
/// The best LCS found so far.
lcs: Lcs,
/// The minimum occurrence count of tokens in the best LCS.
min_occurrences: u32,
/// Whether any common subsequence was found.
found_cs: bool,
}
impl LcsSearch {
fn run(&mut self, before: &[Token], after: &[Token], histogram: &mut Histogram) {
let mut pos = 0;
while let Some(&token) = after.get(pos as usize) {
if histogram.num_token_occurrences(token) != 0 {
self.found_cs = true;
if histogram.num_token_occurrences(token) <= self.min_occurrences {
pos = self.update_lcs(pos, token, histogram, before, after);
continue;
}
}
pos += 1;
}
histogram.clear();
}
fn success(&mut self) -> bool {
!self.found_cs || self.min_occurrences <= MAX_CHAIN_LEN
}
fn update_lcs(
&mut self,
after_pos: u32,
token: Token,
histogram: &Histogram,
before: &[Token],
after: &[Token],
) -> u32 {
let mut next_token_idx2 = after_pos + 1;
let mut occurrences_iter = histogram.token_occurrences(token).iter().copied();
let mut token_idx1 = occurrences_iter.next().unwrap();
'occurrences_iter: loop {
let mut occurrences = histogram.num_token_occurrences(token);
let mut start1 = token_idx1;
let mut start2 = after_pos;
loop {
if start1 == 0 || start2 == 0 {
break;
}
let token1 = before.get(start1 as usize - 1);
let token2 = after.get(start2 as usize - 1);
if matches!((token1, token2), (Some(token1), Some(token2)) if token1 == token2) {
start1 -= 1;
start2 -= 1;
let new_occurrences = histogram.num_token_occurrences(before[start1 as usize]);
occurrences = occurrences.min(new_occurrences);
} else {
break;
}
}
let mut end1 = token_idx1 + 1;
let mut end2 = after_pos + 1;
loop {
let token1 = before.get(end1 as usize);
let token2 = after.get(end2 as usize);
if matches!((token1, token2), (Some(token1), Some(token2)) if token1 == token2) {
let new_occurrences = histogram.num_token_occurrences(before[end1 as usize]);
occurrences = occurrences.min(new_occurrences);
end1 += 1;
end2 += 1;
} else {
break;
}
}
if next_token_idx2 < end2 {
next_token_idx2 = end2;
}
let len = end2 - start2;
debug_assert_eq!(len, end1 - start1);
if self.lcs.len < len || self.min_occurrences > occurrences {
self.min_occurrences = occurrences;
self.lcs = Lcs {
before_start: start1,
after_start: start2,
len,
};
}
loop {
if let Some(next_token_idx) = occurrences_iter.next() {
if next_token_idx > end2 {
token_idx1 = next_token_idx;
break;
}
} else {
break 'occurrences_iter;
}
}
}
next_token_idx2
}
}