Refactor some of the patience code into a utils module

This commit is contained in:
Armin Ronacher 2021-02-21 15:38:08 +01:00
parent 28bfd4698a
commit 77c7654589
3 changed files with 76 additions and 75 deletions

View file

@ -356,17 +356,9 @@ where
if is_empty_range(&old_range) && is_empty_range(&new_range) { if is_empty_range(&old_range) && is_empty_range(&new_range) {
// Do nothing // Do nothing
} else if is_empty_range(&new_range) { } else if is_empty_range(&new_range) {
d.delete( d.delete(old_range.start, old_range.len(), new_range.start)?;
old_range.start,
old_range.end - old_range.start,
new_range.start,
)?;
} else if is_empty_range(&old_range) { } else if is_empty_range(&old_range) {
d.insert( d.insert(old_range.start, new_range.start, new_range.len())?;
old_range.start,
new_range.start,
new_range.end - new_range.start,
)?;
} else if let Some((x_start, y_start)) = find_middle_snake( } else if let Some((x_start, y_start)) = find_middle_snake(
old, old,
old_range.clone(), old_range.clone(),

View file

@ -8,14 +8,14 @@
//! //!
//! This is based on the patience implementation of [pijul](https://pijul.org/) //! This is based on the patience implementation of [pijul](https://pijul.org/)
//! by Pierre-Étienne Meunier. //! by Pierre-Étienne Meunier.
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;
use std::ops::{Index, Range}; use std::ops::{Index, Range};
use std::time::Instant; use std::time::Instant;
use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace}; use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace};
use super::utils::{unique, Indexable};
/// Patience diff algorithm. /// Patience diff algorithm.
/// ///
/// Diff `old`, between indices `old_range` and `new` between indices `new_range`. /// Diff `old`, between indices `old_range` and `new` between indices `new_range`.
@ -57,8 +57,8 @@ where
New::Output: PartialEq<Old::Output> + Hash + Eq, New::Output: PartialEq<Old::Output> + Hash + Eq,
D: DiffHook, D: DiffHook,
{ {
let old_indexes = unique(old, old_range.start, old_range.end); let old_indexes = unique(old, old_range.clone());
let new_indexes = unique(new, new_range.start, new_range.end); let new_indexes = unique(new, new_range.clone());
let mut d = Replace::new(Patience { let mut d = Replace::new(Patience {
d, d,
@ -96,59 +96,6 @@ where
diff(d, old, 0..old.len(), new, 0..new.len()) diff(d, old, 0..old.len(), new, 0..new.len())
} }
struct Indexable<'a, T: ?Sized> {
value: &'a T,
index: usize,
}
impl<'a, T: Index<usize> + 'a> std::fmt::Debug for Indexable<'a, T>
where
T::Output: std::fmt::Debug,
{
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{:?}", &self.value[self.index])
}
}
impl<'a, 'b, A, B> PartialEq<Indexable<'a, A>> for Indexable<'b, B>
where
A: Index<usize> + 'b + ?Sized,
B: Index<usize> + 'b + ?Sized,
B::Output: PartialEq<A::Output>,
{
fn eq(&self, b: &Indexable<'a, A>) -> bool {
self.value[self.index] == b.value[b.index]
}
}
fn unique<T>(seq: &T, lower: usize, upper: usize) -> Vec<Indexable<T>>
where
T: Index<usize> + ?Sized,
T::Output: Hash + Eq,
{
let mut by_item = HashMap::new();
for index in lower..upper {
match by_item.entry(&seq[index]) {
Entry::Vacant(entry) => {
entry.insert(Some(index));
}
Entry::Occupied(mut entry) => {
let entry = entry.get_mut();
if entry.is_some() {
*entry = None
}
}
}
}
let mut rv = by_item
.into_iter()
.filter_map(|(_, x)| x)
.map(|index| Indexable { value: seq, index })
.collect::<Vec<_>>();
rv.sort_by(|a, b| a.index.cmp(&b.index));
rv
}
struct Patience<'old, 'new, 'd, Old: ?Sized, New: ?Sized, D> { struct Patience<'old, 'new, 'd, Old: ?Sized, New: ?Sized, D> {
d: &'d mut D, d: &'d mut D,
old: &'old Old, old: &'old Old,
@ -174,8 +121,8 @@ where
for (old, new) in (old..old + len).zip(new..new + len) { for (old, new) in (old..old + len).zip(new..new + len) {
let a0 = self.old_current; let a0 = self.old_current;
let b0 = self.new_current; let b0 = self.new_current;
while self.old_current < self.old_indexes[old].index while self.old_current < self.old_indexes[old].index()
&& self.new_current < self.new_indexes[new].index && self.new_current < self.new_indexes[new].index()
&& self.new[self.new_current] == self.old[self.old_current] && self.new[self.new_current] == self.old[self.old_current]
{ {
self.old_current += 1; self.old_current += 1;
@ -188,13 +135,13 @@ where
myers::diff_deadline( myers::diff_deadline(
&mut no_finish_d, &mut no_finish_d,
self.old, self.old,
self.old_current..self.old_indexes[old].index, self.old_current..self.old_indexes[old].index(),
self.new, self.new,
self.new_current..self.new_indexes[new].index, self.new_current..self.new_indexes[new].index(),
self.deadline, self.deadline,
)?; )?;
self.old_current = self.old_indexes[old].index; self.old_current = self.old_indexes[old].index();
self.new_current = self.new_indexes[new].index; self.new_current = self.new_indexes[new].index();
} }
Ok(()) Ok(())
} }

View file

@ -1,7 +1,69 @@
use std::ops::Range; use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::{Index, Range};
/// Utility function to check if a range is empty that works on older rust versions /// Utility function to check if a range is empty that works on older rust versions
#[inline(always)] #[inline(always)]
pub(crate) fn is_empty_range<T: PartialOrd>(range: &Range<T>) -> bool { pub fn is_empty_range<T: PartialOrd>(range: &Range<T>) -> bool {
!(range.start < range.end) !(range.start < range.end)
} }
pub struct Indexable<'a, Idx: ?Sized> {
lookup: &'a Idx,
index: usize,
}
impl<'a, Idx: ?Sized> Indexable<'a, Idx> {
/// Returns the index.
pub fn index(&self) -> usize {
self.index
}
}
impl<'a, Idx: Index<usize> + 'a> std::fmt::Debug for Indexable<'a, Idx>
where
Idx::Output: std::fmt::Debug,
{
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{:?}", &self.lookup[self.index])
}
}
impl<'a, 'b, A, B> PartialEq<Indexable<'a, A>> for Indexable<'b, B>
where
A: Index<usize> + 'b + ?Sized,
B: Index<usize> + 'b + ?Sized,
B::Output: PartialEq<A::Output>,
{
fn eq(&self, b: &Indexable<'a, A>) -> bool {
self.lookup[self.index] == b.lookup[b.index]
}
}
pub fn unique<Idx>(seq: &Idx, range: Range<usize>) -> Vec<Indexable<Idx>>
where
Idx: Index<usize> + ?Sized,
Idx::Output: Hash + Eq,
{
let mut by_item = HashMap::new();
for index in range {
match by_item.entry(&seq[index]) {
Entry::Vacant(entry) => {
entry.insert(Some(index));
}
Entry::Occupied(mut entry) => {
let entry = entry.get_mut();
if entry.is_some() {
*entry = None
}
}
}
}
let mut rv = by_item
.into_iter()
.filter_map(|(_, x)| x)
.map(|index| Indexable { lookup: seq, index })
.collect::<Vec<_>>();
rv.sort_by(|a, b| a.index.cmp(&b.index));
rv
}