From 77c765458930c022a67747e23cde50b3218d88ba Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 21 Feb 2021 15:38:08 +0100 Subject: [PATCH] Refactor some of the patience code into a utils module --- src/algorithms/myers.rs | 12 ++----- src/algorithms/patience.rs | 73 ++++++-------------------------------- src/algorithms/utils.rs | 66 ++++++++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/src/algorithms/myers.rs b/src/algorithms/myers.rs index d7b9d38..5e475ea 100644 --- a/src/algorithms/myers.rs +++ b/src/algorithms/myers.rs @@ -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(), diff --git a/src/algorithms/patience.rs b/src/algorithms/patience.rs index c2f6e1a..a24780d 100644 --- a/src/algorithms/patience.rs +++ b/src/algorithms/patience.rs @@ -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 + 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 + '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> for Indexable<'b, B> -where - A: Index + 'b + ?Sized, - B: Index + 'b + ?Sized, - B::Output: PartialEq, -{ - fn eq(&self, b: &Indexable<'a, A>) -> bool { - self.value[self.index] == b.value[b.index] - } -} - -fn unique(seq: &T, lower: usize, upper: usize) -> Vec> -where - T: Index + ?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::>(); - 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(()) } diff --git a/src/algorithms/utils.rs b/src/algorithms/utils.rs index 6c0a693..3071586 100644 --- a/src/algorithms/utils.rs +++ b/src/algorithms/utils.rs @@ -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(range: &Range) -> bool { +pub fn is_empty_range(range: &Range) -> 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 + '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> for Indexable<'b, B> +where + A: Index + 'b + ?Sized, + B: Index + 'b + ?Sized, + B::Output: PartialEq, +{ + fn eq(&self, b: &Indexable<'a, A>) -> bool { + self.lookup[self.index] == b.lookup[b.index] + } +} + +pub fn unique(seq: &Idx, range: Range) -> Vec> +where + Idx: Index + ?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::>(); + rv.sort_by(|a, b| a.index.cmp(&b.index)); + rv +}