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) {
// Do nothing
} else if is_empty_range(&new_range) {
d.delete(
old_range.start,
old_range.end - old_range.start,
new_range.start,
)?;
d.delete(old_range.start, old_range.len(), new_range.start)?;
} else if is_empty_range(&old_range) {
d.insert(
old_range.start,
new_range.start,
new_range.end - new_range.start,
)?;
d.insert(old_range.start, new_range.start, new_range.len())?;
} else if let Some((x_start, y_start)) = find_middle_snake(
old,
old_range.clone(),

View file

@ -8,14 +8,14 @@
//!
//! This is based on the patience implementation of [pijul](https://pijul.org/)
//! by Pierre-Étienne Meunier.
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::{Index, Range};
use std::time::Instant;
use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace};
use super::utils::{unique, Indexable};
/// Patience diff algorithm.
///
/// Diff `old`, between indices `old_range` and `new` between indices `new_range`.
@ -57,8 +57,8 @@ where
New::Output: PartialEq<Old::Output> + Hash + Eq,
D: DiffHook,
{
let old_indexes = unique(old, old_range.start, old_range.end);
let new_indexes = unique(new, new_range.start, new_range.end);
let old_indexes = unique(old, old_range.clone());
let new_indexes = unique(new, new_range.clone());
let mut d = Replace::new(Patience {
d,
@ -96,59 +96,6 @@ where
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> {
d: &'d mut D,
old: &'old Old,
@ -174,8 +121,8 @@ where
for (old, new) in (old..old + len).zip(new..new + len) {
let a0 = self.old_current;
let b0 = self.new_current;
while self.old_current < self.old_indexes[old].index
&& self.new_current < self.new_indexes[new].index
while self.old_current < self.old_indexes[old].index()
&& self.new_current < self.new_indexes[new].index()
&& self.new[self.new_current] == self.old[self.old_current]
{
self.old_current += 1;
@ -188,13 +135,13 @@ where
myers::diff_deadline(
&mut no_finish_d,
self.old,
self.old_current..self.old_indexes[old].index,
self.old_current..self.old_indexes[old].index(),
self.new,
self.new_current..self.new_indexes[new].index,
self.new_current..self.new_indexes[new].index(),
self.deadline,
)?;
self.old_current = self.old_indexes[old].index;
self.new_current = self.new_indexes[new].index;
self.old_current = self.old_indexes[old].index();
self.new_current = self.new_indexes[new].index();
}
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
#[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)
}
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
}