1use std::collections::HashMap;
16
17use crate::parse::{Attr, Child, Element, File, Value};
18
19pub const MAX_EDITS: usize = 256;
21
22pub fn matched(mut new: File, last: &File) -> File {
25 let mut next = last.next_id;
26 if new.root.name == last.root.name {
27 pair(&mut new.root, &last.root, &mut next);
28 } else {
29 fresh(&mut new.root, &mut next);
30 }
31 new.next_id = next;
32 new
33}
34
35pub fn key(el: &Element) -> Option<&str> {
37 el.attrs.iter().find_map(|a| match (&*a.name, &a.value) {
38 ("key", Value::Str(s, _)) => Some(s.as_str()),
39 _ => None,
40 })
41}
42
43fn fresh(el: &mut Element, next: &mut u32) {
44 el.id = *next;
45 *next += 1;
46 for child in &mut el.children {
47 if let Child::Element(e) = child {
48 fresh(e, next);
49 }
50 }
51}
52
53fn pair(new: &mut Element, old: &Element, next: &mut u32) {
54 new.id = old.id;
55 let olds: Vec<&Element> = elements(&old.children).collect();
56 let mut news: Vec<&mut Element> = new
57 .children
58 .iter_mut()
59 .filter_map(|c| match c {
60 Child::Element(e) => Some(e),
61 Child::Text(..) => None,
62 })
63 .collect();
64 let matches = siblings(&news.iter().map(|e| &**e).collect::<Vec<_>>(), &olds);
65 for (n, el) in news.iter_mut().enumerate() {
66 match matches[n] {
67 Some(o) => pair(el, olds[o], next),
68 None => fresh(el, next),
69 }
70 }
71}
72
73fn elements(children: &[Child]) -> impl Iterator<Item = &Element> {
74 children.iter().filter_map(|c| match c {
75 Child::Element(e) => Some(e),
76 Child::Text(..) => None,
77 })
78}
79
80fn siblings(news: &[&Element], olds: &[&Element]) -> Vec<Option<usize>> {
82 let mut out = vec![None; news.len()];
83 let mut taken = vec![false; olds.len()];
84 let mut keyed: HashMap<(&str, &str), usize> = HashMap::new();
86 for (o, el) in olds.iter().enumerate() {
87 if let Some(k) = key(el) {
88 keyed.entry((&el.name, k)).or_insert(o);
89 }
90 }
91 for (n, el) in news.iter().enumerate() {
92 if let Some(k) = key(el)
93 && let Some(&o) = keyed.get(&(el.name.as_str(), k))
94 && !taken[o]
95 {
96 out[n] = Some(o);
97 taken[o] = true;
98 }
99 }
100 let free_new: Vec<usize> = (0..news.len())
102 .filter(|&n| out[n].is_none() && key(news[n]).is_none())
103 .collect();
104 let free_old: Vec<usize> = (0..olds.len())
105 .filter(|&o| !taken[o] && key(olds[o]).is_none())
106 .collect();
107 let shapes_new: Vec<u64> = free_new.iter().map(|&n| shape(news[n])).collect();
111 let shapes_old: Vec<u64> = free_old.iter().map(|&o| shape(olds[o])).collect();
112 let same = |n: usize, o: usize| {
113 shapes_new[n] == shapes_old[o] && same_own(news[free_new[n]], olds[free_old[o]])
114 };
115 let anchors = common(free_new.len(), free_old.len(), &same);
116 let mut last = (0, 0);
118 let ends = anchors
119 .iter()
120 .copied()
121 .chain([(free_new.len(), free_old.len())]);
122 for (an, ao) in ends {
123 let mut olds_left: Vec<usize> = (last.1..ao).collect();
124 for n in last.0..an {
125 let name = &news[free_new[n]].name;
126 if let Some(at) = olds_left
127 .iter()
128 .position(|&o| olds[free_old[o]].name == *name)
129 {
130 let o = olds_left.remove(at);
131 out[free_new[n]] = Some(free_old[o]);
132 olds_left.drain(..at);
134 }
135 }
136 if an < free_new.len() {
137 out[free_new[an]] = Some(free_old[ao]);
138 }
139 last = (an + 1, ao + 1);
140 }
141 out
142}
143
144fn common(n: usize, m: usize, same: &dyn Fn(usize, usize) -> bool) -> Vec<(usize, usize)> {
149 let mut pre = 0;
150 while pre < n && pre < m && same(pre, pre) {
151 pre += 1;
152 }
153 let mut suf = 0;
154 while suf < n - pre && suf < m - pre && same(n - 1 - suf, m - 1 - suf) {
155 suf += 1;
156 }
157 let mut out: Vec<(usize, usize)> = (0..pre).map(|i| (i, i)).collect();
158 let (a, b) = (n - pre - suf, m - pre - suf);
159 let mid = myers(a, b, &|i, j| same(pre + i, pre + j));
160 out.extend(mid.into_iter().map(|(i, j)| (pre + i, pre + j)));
161 out.extend((0..suf).map(|i| (n - suf + i, m - suf + i)));
162 out
163}
164
165fn myers(n: usize, m: usize, same: &dyn Fn(usize, usize) -> bool) -> Vec<(usize, usize)> {
168 if n == 0 || m == 0 {
169 return Vec::new();
170 }
171 let max = (n + m).min(MAX_EDITS);
172 let off = max as isize + 1;
173 let mut v = vec![0isize; 2 * max + 3];
174 let mut trace: Vec<Vec<isize>> = Vec::new();
175 for d in 0..=max as isize {
176 trace.push(v.clone());
177 let mut k = -d;
178 while k <= d {
179 let at = (k + off) as usize;
180 let mut x = if k == -d || (k != d && v[at - 1] < v[at + 1]) {
181 v[at + 1]
182 } else {
183 v[at - 1] + 1
184 };
185 let mut y = x - k;
186 while (x as usize) < n && y >= 0 && (y as usize) < m && same(x as usize, y as usize) {
187 x += 1;
188 y += 1;
189 }
190 v[at] = x;
191 if x >= n as isize && y >= m as isize {
192 return backtrack(&trace, d, n, m, off);
193 }
194 k += 2;
195 }
196 }
197 Vec::new()
198}
199
200fn backtrack(
203 trace: &[Vec<isize>],
204 d: isize,
205 n: usize,
206 m: usize,
207 off: isize,
208) -> Vec<(usize, usize)> {
209 let mut out = Vec::new();
210 let (mut x, mut y) = (n as isize, m as isize);
211 for d in (1..=d).rev() {
212 let v = &trace[d as usize];
213 let k = x - y;
214 let at = |k: isize| v[(k + off) as usize];
215 let prev_k = if k == -d || (k != d && at(k - 1) < at(k + 1)) {
216 k + 1
217 } else {
218 k - 1
219 };
220 let prev_x = at(prev_k);
221 let prev_y = prev_x - prev_k;
222 let (sx, sy) = if prev_k == k + 1 {
224 (prev_x, prev_y + 1)
225 } else {
226 (prev_x + 1, prev_y)
227 };
228 while x > sx && y > sy {
229 x -= 1;
230 y -= 1;
231 out.push((x as usize, y as usize));
232 }
233 x = prev_x;
234 y = prev_y;
235 }
236 while x > 0 && y > 0 {
237 x -= 1;
238 y -= 1;
239 out.push((x as usize, y as usize));
240 }
241 out.reverse();
242 out
243}
244
245fn shape(el: &Element) -> u64 {
248 use std::hash::{Hash, Hasher};
249 fn value(v: &Value, h: &mut std::collections::hash_map::DefaultHasher) {
250 match v {
251 Value::Str(s, _) => (0u8, s).hash(h),
252 Value::Theme(s, _) => (1u8, s).hash(h),
253 Value::Num(n) => (2u8, n.to_bits()).hash(h),
254 Value::Bool(b) => (3u8, b).hash(h),
255 Value::Prop(p) => (4u8, p).hash(h),
256 Value::Array(items) => {
257 (5u8, items.len()).hash(h);
258 for i in items {
259 value(i, h);
260 }
261 }
262 Value::Object(fields) => {
263 (6u8, fields.len()).hash(h);
264 for (k, v) in fields {
265 k.hash(h);
266 value(v, h);
267 }
268 }
269 other => format!("{other:?}").hash(h),
270 }
271 }
272 fn walk(el: &Element, h: &mut std::collections::hash_map::DefaultHasher) {
273 el.name.hash(h);
274 el.attrs.len().hash(h);
275 for a in &el.attrs {
276 a.name.hash(h);
277 value(&a.value, h);
278 }
279 el.children.len().hash(h);
280 for c in &el.children {
281 match c {
282 Child::Element(e) => walk(e, h),
283 Child::Text(parts, _) => {
284 for p in parts {
285 match p {
286 crate::parse::TextPart::Literal(s) => (0u8, s).hash(h),
287 crate::parse::TextPart::Prop(s) => (1u8, s).hash(h),
288 crate::parse::TextPart::Theme(s, _) => (2u8, s).hash(h),
289 }
290 }
291 }
292 }
293 }
294 }
295 let mut h = std::collections::hash_map::DefaultHasher::new();
296 walk(el, &mut h);
297 h.finish()
298}
299
300pub(crate) fn same_own(a: &Element, b: &Element) -> bool {
304 a.name == b.name
305 && a.attrs.len() == b.attrs.len()
306 && a.attrs.iter().zip(&b.attrs).all(|(x, y)| same_attr(x, y))
307}
308
309fn same_attr(a: &Attr, b: &Attr) -> bool {
310 a.name == b.name && same_value(&a.value, &b.value)
311}
312
313pub(crate) fn same_value(a: &Value, b: &Value) -> bool {
314 match (a, b) {
315 (Value::Str(x, _), Value::Str(y, _)) => x == y,
316 (Value::Theme(x, _), Value::Theme(y, _)) => x == y,
317 (Value::Array(x), Value::Array(y)) => {
318 x.len() == y.len() && x.iter().zip(y).all(|(a, b)| same_value(a, b))
319 }
320 (Value::Object(x), Value::Object(y)) => {
321 x.len() == y.len()
322 && x.iter()
323 .zip(y)
324 .all(|((ka, a), (kb, b))| ka == kb && same_value(a, b))
325 }
326 _ => a == b,
327 }
328}
329
330#[cfg(test)]
331mod tests;
332
333#[cfg(test)]
334pub(crate) mod testing;