139 lines · 4.7 KB · Rust
RawDownload
// 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    }}