Reuse common prefix / common suffix functions

This commit is contained in:
Armin Ronacher 2021-02-21 19:52:10 +01:00
parent b468bf94e3
commit 1a0aa9b142
3 changed files with 71 additions and 79 deletions

View file

@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use std::ops::{Index, Range}; use std::ops::{Index, Range};
use std::time::Instant; use std::time::Instant;
use crate::algorithms::utils::is_empty_range; use crate::algorithms::utils::{common_prefix_len, common_suffix_len, is_empty_range};
use crate::algorithms::DiffHook; use crate::algorithms::DiffHook;
/// HuntMcIlroy / HuntSzymanski LCS diff algorithm. /// HuntMcIlroy / HuntSzymanski LCS diff algorithm.
@ -61,39 +61,29 @@ where
return Ok(()); return Ok(());
} }
let prefix_len = old_range let common_prefix_len = common_prefix_len(old, old_range.clone(), new, new_range.clone());
.clone() let common_suffix_len = common_suffix_len(old, old_range.clone(), new, new_range.clone());
.zip(new_range.clone())
.take_while(|x| new[x.1] == old[x.0])
.count();
let suffix_len = old_range
.clone()
.rev()
.zip(new_range.clone().rev())
.take(old_range.len().min(new_range.len()) - prefix_len)
.take_while(|x| new[x.1] == old[x.0])
.count();
let maybe_table = make_table( let maybe_table = make_table(
old, old,
prefix_len..(old_range.len() - suffix_len), common_prefix_len..(old_range.len() - common_suffix_len),
new, new,
prefix_len..(new_range.len() - suffix_len), common_prefix_len..(new_range.len() - common_suffix_len),
deadline, deadline,
); );
let mut old_idx = 0; let mut old_idx = 0;
let mut new_idx = 0; let mut new_idx = 0;
let new_len = new_range.len() - prefix_len - suffix_len; let new_len = new_range.len() - common_prefix_len - common_suffix_len;
let old_len = old_range.len() - prefix_len - suffix_len; let old_len = old_range.len() - common_prefix_len - common_suffix_len;
if prefix_len > 0 { if common_prefix_len > 0 {
d.equal(old_range.start, new_range.start, prefix_len)?; d.equal(old_range.start, new_range.start, common_prefix_len)?;
} }
if let Some(table) = maybe_table { if let Some(table) = maybe_table {
while new_idx < new_len && old_idx < old_len { while new_idx < new_len && old_idx < old_len {
let old_orig_idx = old_range.start + prefix_len + old_idx; let old_orig_idx = old_range.start + common_prefix_len + old_idx;
let new_orig_idx = new_range.start + prefix_len + new_idx; let new_orig_idx = new_range.start + common_prefix_len + new_idx;
if new[new_orig_idx] == old[old_orig_idx] { if new[new_orig_idx] == old[old_orig_idx] {
d.equal(old_orig_idx, new_orig_idx, 1)?; d.equal(old_orig_idx, new_orig_idx, 1)?;
@ -110,34 +100,34 @@ where
} }
} }
} else { } else {
let old_orig_idx = old_range.start + prefix_len + old_idx; let old_orig_idx = old_range.start + common_prefix_len + old_idx;
let new_orig_idx = new_range.start + prefix_len + new_idx; let new_orig_idx = new_range.start + common_prefix_len + new_idx;
d.delete(old_orig_idx, old_len, new_orig_idx)?; d.delete(old_orig_idx, old_len, new_orig_idx)?;
d.insert(old_orig_idx, new_orig_idx, new_len)?; d.insert(old_orig_idx, new_orig_idx, new_len)?;
} }
if old_idx < old_len { if old_idx < old_len {
d.delete( d.delete(
old_range.start + prefix_len + old_idx, old_range.start + common_prefix_len + old_idx,
old_len - old_idx, old_len - old_idx,
new_range.start + prefix_len + new_idx, new_range.start + common_prefix_len + new_idx,
)?; )?;
old_idx += old_len - old_idx; old_idx += old_len - old_idx;
} }
if new_idx < new_len { if new_idx < new_len {
d.insert( d.insert(
old_range.start + prefix_len + old_idx, old_range.start + common_prefix_len + old_idx,
new_range.start + prefix_len + new_idx, new_range.start + common_prefix_len + new_idx,
new_len - new_idx, new_len - new_idx,
)?; )?;
} }
if suffix_len > 0 { if common_suffix_len > 0 {
d.equal( d.equal(
old_range.start + old_len + prefix_len, old_range.start + old_len + common_prefix_len,
new_range.start + new_len + prefix_len, new_range.start + new_len + common_prefix_len,
suffix_len, common_suffix_len,
)?; )?;
} }

View file

@ -22,7 +22,7 @@
use std::ops::{Index, IndexMut, Range}; use std::ops::{Index, IndexMut, Range};
use std::time::Instant; use std::time::Instant;
use crate::algorithms::utils::is_empty_range; use crate::algorithms::utils::{common_prefix_len, common_suffix_len, is_empty_range};
use crate::algorithms::DiffHook; use crate::algorithms::DiffHook;
/// Myers' diff algorithm. /// Myers' diff algorithm.
@ -138,53 +138,6 @@ fn max_d(len1: usize, len2: usize) -> usize {
(len1 + len2 + 1) / 2 + 1 (len1 + len2 + 1) / 2 + 1
} }
fn common_prefix_len<Old, New>(
old: &Old,
old_range: Range<usize>,
new: &New,
new_range: Range<usize>,
) -> usize
where
Old: Index<usize> + ?Sized,
New: Index<usize> + ?Sized,
New::Output: PartialEq<Old::Output>,
{
if is_empty_range(&old_range) || is_empty_range(&new_range) {
return 0;
}
new_range
.zip(old_range)
.take_while(
#[inline(always)]
|x| new[x.0] == old[x.1],
)
.count()
}
fn common_suffix_len<Old, New>(
old: &Old,
old_range: Range<usize>,
new: &New,
new_range: Range<usize>,
) -> usize
where
Old: Index<usize> + ?Sized,
New: Index<usize> + ?Sized,
New::Output: PartialEq<Old::Output>,
{
if is_empty_range(&old_range) || is_empty_range(&new_range) {
return 0;
}
new_range
.rev()
.zip(old_range.rev())
.take_while(
#[inline(always)]
|x| new[x.0] == old[x.1],
)
.count()
}
#[inline(always)] #[inline(always)]
fn split_at(range: Range<usize>, at: usize) -> (Range<usize>, Range<usize>) { fn split_at(range: Range<usize>, at: usize) -> (Range<usize>, Range<usize>) {
(range.start..at, at..range.end) (range.start..at, at..range.end)

View file

@ -93,6 +93,55 @@ where
rv rv
} }
/// Given two lookups and ranges calculates the length of the common prefix.
pub fn common_prefix_len<Old, New>(
old: &Old,
old_range: Range<usize>,
new: &New,
new_range: Range<usize>,
) -> usize
where
Old: Index<usize> + ?Sized,
New: Index<usize> + ?Sized,
New::Output: PartialEq<Old::Output>,
{
if is_empty_range(&old_range) || is_empty_range(&new_range) {
return 0;
}
new_range
.zip(old_range)
.take_while(
#[inline(always)]
|x| new[x.0] == old[x.1],
)
.count()
}
/// Given two lookups and ranges calculates the length of common suffix.
pub fn common_suffix_len<Old, New>(
old: &Old,
old_range: Range<usize>,
new: &New,
new_range: Range<usize>,
) -> usize
where
Old: Index<usize> + ?Sized,
New: Index<usize> + ?Sized,
New::Output: PartialEq<Old::Output>,
{
if is_empty_range(&old_range) || is_empty_range(&new_range) {
return 0;
}
new_range
.rev()
.zip(old_range.rev())
.take_while(
#[inline(always)]
|x| new[x.0] == old[x.1],
)
.count()
}
#[test] #[test]
fn test_unique() { fn test_unique() {
let u = unique(&vec!['a', 'b', 'c', 'd', 'd', 'b'], 0..6) let u = unique(&vec!['a', 'b', 'c', 'd', 'd', 'b'], 0..6)