Skip to main content

devpage_design/wire/
patch.rs

1//! Patches: the operations that turn one scene into the next, each naming
2//! its nodes by id — a node's own attributes changed, a subtree inserted
3//! after a sibling, a subtree removed, a subtree moved — and applying them to
4//! a scene, all or nothing, at the cost of the operations alone.
5
6use std::collections::{HashMap, HashSet};
7
8use super::{
9    Uses, decode, encode, encode_own, file, files, font_file, font_files, node_id, unnode_id,
10};
11use crate::proto::{self, op::Op as Kind};
12use crate::resolve::{Node, Scene};
13use crate::tree::{NodeId, Order, OrderKey};
14
15/// The patch that turns `old`, as last sent, into `new`, by their nodes'
16/// ids: an insert before the first of 100,000 siblings is one insert. And the
17/// image and font files `old` didn't have.
18///
19/// It compares the two scenes whole; the store finds most patches without
20/// one, from what a save reached.
21pub fn patch(old: &Scene, new: &Scene) -> proto::Patch {
22    proto::Patch {
23        ops: encode_edits(&edits(old, new)),
24        images: new
25            .images
26            .values()
27            .filter(|f| !old.images.contains_key(&f.id))
28            .map(|f| file(f))
29            .collect(),
30        fonts: new
31            .fonts
32            .values()
33            .filter(|f| !old.fonts.contains_key(&f.id))
34            .map(|f| font_file(f))
35            .collect(),
36        themed: new.themed,
37    }
38}
39
40fn op(kind: Kind) -> proto::Op {
41    proto::Op { op: Some(kind) }
42}
43
44/// One operation of a patch, on the engine's own nodes: what the wire
45/// carries as a [`proto::Op`], and what a scene kept beside a resolve is
46/// brought across by without its nodes passing through the wire's form.
47#[derive(Debug, Clone, PartialEq)]
48pub enum Edit {
49    /// The node of this id takes these own attributes; its children stay.
50    Change(Node),
51    /// This subtree goes under the node `parent`, after its child `after`
52    /// or first — or, with no parent, replaces the scene.
53    Insert {
54        parent: Option<NodeId>,
55        after: Option<NodeId>,
56        node: Node,
57    },
58    /// The subtree of this id goes.
59    Remove(NodeId),
60    /// The subtree of this id moves under `parent`, after `after` or first.
61    Move {
62        id: NodeId,
63        parent: NodeId,
64        after: Option<NodeId>,
65    },
66}
67
68/// Edits in the wire's form.
69pub fn encode_edits(edits: &[Edit]) -> Vec<proto::Op> {
70    edits
71        .iter()
72        .map(|e| {
73            op(match e {
74                Edit::Change(n) => Kind::Change(encode_own(n)),
75                Edit::Insert {
76                    parent,
77                    after,
78                    node,
79                } => Kind::Insert(proto::Insert {
80                    parent: parent.as_ref().map(node_id),
81                    after: after.as_ref().map(node_id),
82                    node: Some(encode(node)),
83                }),
84                Edit::Remove(id) => Kind::Remove(node_id(id)),
85                Edit::Move { id, parent, after } => Kind::Move(proto::Move {
86                    id: Some(node_id(id)),
87                    parent: Some(node_id(parent)),
88                    after: after.as_ref().map(node_id),
89                }),
90            })
91        })
92        .collect()
93}
94
95/// Edits from the wire's form, or why they aren't.
96fn decode_edits(ops: &[proto::Op]) -> Result<Vec<Edit>, String> {
97    ops.iter()
98        .map(|o| {
99            Ok(match o.op.as_ref().ok_or("an empty operation")? {
100                Kind::Change(n) => Edit::Change(decode(n)?),
101                Kind::Insert(i) => Edit::Insert {
102                    parent: i.parent.as_ref().map(|p| unnode_id(Some(p))).transpose()?,
103                    after: i.after.as_ref().map(|a| unnode_id(Some(a))).transpose()?,
104                    node: decode(i.node.as_ref().ok_or("an insert with no node")?)?,
105                },
106                Kind::Remove(id) => Edit::Remove(unnode_id(Some(id))?),
107                Kind::Move(m) => Edit::Move {
108                    id: unnode_id(m.id.as_ref())?,
109                    parent: unnode_id(m.parent.as_ref())?,
110                    after: m.after.as_ref().map(|a| unnode_id(Some(a))).transpose()?,
111                },
112            })
113        })
114        .collect()
115}
116
117/// The operations from `old` to `new`. Each node of `new` that `old` has is
118/// kept — moved if it has another parent or falls out of the longest run of
119/// siblings still in their order, and changed if its own attributes did;
120/// each one `old` hasn't is inserted, whole where nothing under it is kept,
121/// alone where something is, which is then moved into it. Last, what `old`
122/// has and `new` doesn't goes: by then nothing kept is under it.
123pub fn edits(old: &Scene, new: &Scene) -> Vec<Edit> {
124    if old.root.id != new.root.id {
125        return vec![Edit::Insert {
126            parent: None,
127            after: None,
128            node: new.root.clone(),
129        }];
130    }
131    subtree(old, &[], &new.root)
132}
133
134/// The edits that turn the subtree of `old` at `at` — whose root has the id
135/// `new`'s root has — into `new`: what a part of a scene resolved again is
136/// brought across by, costing that part alone.
137pub fn subtree(old: &Scene, at: &[OrderKey], new: &Node) -> Vec<Edit> {
138    let within = |id: &NodeId| old.ids.get(id).is_some_and(|o| o.starts_with(at));
139    let mut out = Vec::new();
140    // Under each node of `new`, whether something `old` has is.
141    fn kept_below(n: &Node, within: &dyn Fn(&NodeId) -> bool, out: &mut HashSet<NodeId>) -> bool {
142        let mut any = false;
143        for c in &n.children {
144            any |= kept_below(c, within, out) | within(&c.id);
145        }
146        if any {
147            out.insert(n.id.clone());
148        }
149        any
150    }
151    let mut holds = HashSet::new();
152    kept_below(new, &within, &mut holds);
153    let Some(was) = old.at(at) else {
154        return out;
155    };
156    if !was.same_own(new) {
157        out.push(Edit::Change(new.own()));
158    }
159    let mut todo = vec![new];
160    while let Some(p) = todo.pop() {
161        // The children `old` had under this same parent, by their places
162        // there: those in the longest run still in order stay.
163        let old_parent = old
164            .ids
165            .get(&p.id)
166            .filter(|o| o.starts_with(at))
167            .and_then(|o| old.at(o));
168        let rank: HashMap<&NodeId, usize> = old_parent
169            .map(|op| {
170                op.children
171                    .iter()
172                    .enumerate()
173                    .map(|(i, c)| (&c.id, i))
174                    .collect()
175            })
176            .unwrap_or_default();
177        let ranks: Vec<Option<usize>> = p
178            .children
179            .iter()
180            .map(|c| rank.get(&c.id).copied())
181            .collect();
182        let stay = longest_run(&ranks);
183        let mut prev: Option<&NodeId> = None;
184        for (i, c) in p.children.iter().enumerate() {
185            match old.node(&c.id).filter(|_| within(&c.id)) {
186                Some(was) => {
187                    if !stay[i] {
188                        out.push(Edit::Move {
189                            id: c.id.clone(),
190                            parent: p.id.clone(),
191                            after: prev.cloned(),
192                        });
193                    }
194                    if !was.same_own(c) {
195                        out.push(Edit::Change(c.own()));
196                    }
197                    todo.push(c);
198                }
199                None => {
200                    let node = if holds.contains(&c.id) {
201                        todo.push(c);
202                        c.own()
203                    } else {
204                        c.clone()
205                    };
206                    out.push(Edit::Insert {
207                        parent: Some(p.id.clone()),
208                        after: prev.cloned(),
209                        node,
210                    });
211                }
212            }
213            prev = Some(&c.id);
214        }
215    }
216    let kept: HashSet<&NodeId> = {
217        fn ids<'a>(n: &'a Node, out: &mut HashSet<&'a NodeId>) {
218            out.insert(&n.id);
219            for c in &n.children {
220                ids(c, out);
221            }
222        }
223        let mut out = HashSet::new();
224        ids(new, &mut out);
225        out
226    };
227    fn gone(n: &Node, kept: &HashSet<&NodeId>, out: &mut Vec<Edit>) {
228        for c in &n.children {
229            if kept.contains(&c.id) {
230                gone(c, kept, out);
231            } else {
232                out.push(Edit::Remove(c.id.clone()));
233            }
234        }
235    }
236    gone(was, &kept, &mut out);
237    out
238}
239
240/// Which of a list stay where they are: the longest increasing run of their
241/// places before, `None` for those that weren't there.
242fn longest_run(ranks: &[Option<usize>]) -> Vec<bool> {
243    // Patience sorting: the smallest last place of a run of each length,
244    // and each element's predecessor in its run.
245    let mut tails: Vec<usize> = Vec::new();
246    let mut before: Vec<Option<usize>> = vec![None; ranks.len()];
247    for (i, r) in ranks.iter().enumerate() {
248        let Some(r) = r else { continue };
249        let at = tails.partition_point(|&t| ranks[t].expect("ranked") < *r);
250        before[i] = at.checked_sub(1).map(|a| tails[a]);
251        if at == tails.len() {
252            tails.push(i);
253        } else {
254            tails[at] = i;
255        }
256    }
257    let mut stay = vec![false; ranks.len()];
258    let mut at = tails.last().copied();
259    while let Some(i) = at {
260        stay[i] = true;
261        at = before[i];
262    }
263    stay
264}
265
266/// What a patch did, by id, for a drawing kept beside the scene to follow:
267/// the subtrees that left their place — removed, or moved away — the
268/// subtrees placed — inserted, or moved here — and the nodes that kept their
269/// place and took new own attributes, with the ones they had.
270#[derive(Debug, Default, Clone, PartialEq)]
271pub struct Applied {
272    pub removed: Vec<NodeId>,
273    pub placed: Vec<NodeId>,
274    pub changed: Vec<(NodeId, Node)>,
275}
276
277/// One operation done, and how to undo it.
278enum Undo {
279    /// Put back these own attributes at this order.
280    Own(Order, Node),
281    /// Take out the subtree at this order.
282    Take(Order),
283    /// Put this subtree back under the parent at this order, under this key.
284    Put(Order, OrderKey, Node),
285    /// Move the subtree now at this order back under this parent and key.
286    Back(Order, Order, OrderKey),
287    /// Put this root back.
288    Root(Node),
289}
290
291/// Applies a patch to `scene`, whose files `uses` counts, or says why it
292/// can't — leaving both as they were. The work is the patch's: each
293/// operation finds its nodes by id, and the files no node uses any more are
294/// dropped by count, never by a walk of the scene.
295pub fn apply(scene: &mut Scene, uses: &mut Uses, patch: &proto::Patch) -> Result<Applied, String> {
296    let images = files(&patch.images, &scene.images)?;
297    let fonts = font_files(&patch.fonts, &scene.fonts)?;
298    let edits = decode_edits(&patch.ops)?;
299    apply_edits(scene, uses, edits, images, fonts, patch.themed)
300}
301
302/// Brings `scene`, whose files `uses` counts, to `to`'s content by the
303/// patch between them — its nodes keeping their places, so what is kept
304/// beside it can follow — with `to`'s image and font files as they are,
305/// never through the wire's bytes.
306pub fn bring(scene: &mut Scene, uses: &mut Uses, to: &Scene) -> Result<Applied, String> {
307    let edits = edits(scene, to);
308    let images = to.images.clone();
309    let fonts = to.fonts.clone();
310    apply_edits(scene, uses, edits, images, fonts, to.themed)
311}
312
313/// Applies `edits` to `scene`, whose files `uses` counts, with the image and
314/// font files they may need — or says why it can't, leaving both as they
315/// were. The work is the edits': each finds its nodes by id.
316pub fn apply_edits(
317    scene: &mut Scene,
318    uses: &mut Uses,
319    ops: Vec<Edit>,
320    images: crate::resolve::ImageFiles,
321    fonts: crate::fonts::FontFiles,
322    themed: bool,
323) -> Result<Applied, String> {
324    let mut undo = Vec::new();
325    let mut applied = Applied::default();
326    let done =
327        run(scene, uses, ops, &mut undo, &mut applied).and_then(|()| {
328            // Every file a node uses is held or arrives with the patch.
329            if let Some(missing) = uses.images.iter().find(|(id, c)| {
330                **c > 0 && !scene.images.contains_key(id) && !images.contains_key(id)
331            }) {
332                return Err(format!(
333                    "the stream never sent the image file {:?}",
334                    missing.0
335                ));
336            }
337            if let Some(missing) = uses
338                .fonts
339                .iter()
340                .find(|(id, c)| **c > 0 && !scene.fonts.contains_key(id) && !fonts.contains_key(id))
341            {
342                return Err(format!(
343                    "the stream never sent the font file {:?}",
344                    missing.0
345                ));
346            }
347            Ok(())
348        });
349    if let Err(e) = done {
350        for u in undo.into_iter().rev() {
351            revert(scene, uses, u);
352        }
353        uses.settle();
354        return Err(e);
355    }
356    scene.images.extend(images);
357    scene.fonts.extend(fonts);
358    uses.settle();
359    scene.images.retain(|id, _| uses.images.contains_key(id));
360    scene.fonts.retain(|id, _| uses.fonts.contains_key(id));
361    scene.themed = themed;
362    Ok(applied)
363}
364
365fn run(
366    scene: &mut Scene,
367    uses: &mut Uses,
368    ops: Vec<Edit>,
369    undo: &mut Vec<Undo>,
370    applied: &mut Applied,
371) -> Result<(), String> {
372    for o in ops {
373        match o {
374            Edit::Change(new) => {
375                let order = order_of(scene, &new.id)?;
376                let place = scene.at_mut(&order).expect("indexed");
377                let mut own = std::mem::replace(place, new);
378                std::mem::swap(&mut place.children, &mut own.children);
379                uses.take_own(&own);
380                uses.add_own(place);
381                applied.changed.push((own.id.clone(), own.clone()));
382                undo.push(Undo::Own(order, own));
383            }
384            Edit::Insert {
385                parent,
386                after,
387                node,
388            } => {
389                let Some(parent) = parent.as_ref() else {
390                    // A new root: the scene, replaced.
391                    if crate::tree::Ids::of(&node).len() != count(&node) {
392                        return Err(format!("{} holds a node twice", node.id));
393                    }
394                    let old = std::mem::replace(&mut scene.root, node);
395                    uses.take(&old);
396                    uses.add(&scene.root);
397                    scene.ids = crate::tree::Ids::of(&scene.root);
398                    applied.removed.push(old.id.clone());
399                    applied.placed.push(scene.root.id.clone());
400                    undo.push(Undo::Root(old));
401                    continue;
402                };
403                let parent = order_of(scene, parent)?;
404                let after = after_key(scene, &parent, after.as_ref())?;
405                check_unique(scene, &node, None)?;
406                uses.add(&node);
407                let id = node.id.clone();
408                let order = place(scene, &parent, after.as_ref(), node);
409                applied.placed.push(id);
410                undo.push(Undo::Take(order));
411            }
412            Edit::Remove(id) => {
413                let order = order_of(scene, &id)?;
414                if order.is_empty() {
415                    return Err("the root can't be removed".into());
416                }
417                let (parent, key) = (
418                    order[..order.len() - 1].to_vec(),
419                    order[order.len() - 1].clone(),
420                );
421                let node = take(scene, &order);
422                uses.take(&node);
423                applied.removed.push(id);
424                undo.push(Undo::Put(parent, key, node));
425            }
426            Edit::Move { id, parent, after } => {
427                let from = order_of(scene, &id)?;
428                if from.is_empty() {
429                    return Err("the root can't be moved".into());
430                }
431                let parent = order_of(scene, &parent)?;
432                if parent.starts_with(&from) {
433                    return Err(format!("{id} can't move under itself"));
434                }
435                let key = from[from.len() - 1].clone();
436                let old_parent = from[..from.len() - 1].to_vec();
437                let after = after_key(scene, &parent, after.as_ref())?;
438                if after.as_ref() == Some(&key) && parent == old_parent {
439                    return Err(format!("{id} can't move after itself"));
440                }
441                let node = take(scene, &from);
442                let to = place(scene, &parent, after.as_ref(), node);
443                applied.removed.push(id.clone());
444                applied.placed.push(id);
445                undo.push(Undo::Back(to, old_parent, key));
446            }
447        }
448    }
449    Ok(())
450}
451
452fn count(n: &Node) -> usize {
453    1 + n.children.iter().map(count).sum::<usize>()
454}
455
456fn order_of(scene: &Scene, id: &NodeId) -> Result<Order, String> {
457    scene
458        .ids
459        .get(id)
460        .cloned()
461        .ok_or_else(|| format!("no node {id}"))
462}
463
464/// The key, under the parent at `parent`, of its child `after`.
465fn after_key(
466    scene: &Scene,
467    parent: &Order,
468    after: Option<&NodeId>,
469) -> Result<Option<OrderKey>, String> {
470    let Some(id) = after else { return Ok(None) };
471    let o = order_of(scene, id)?;
472    if o.len() != parent.len() + 1 || !o.starts_with(parent) {
473        return Err(format!("{id} isn't a child of the node it is placed under"));
474    }
475    Ok(o.last().cloned())
476}
477
478/// Says if `node`'s subtree names a node the scene has, but for `but`.
479fn check_unique(scene: &Scene, node: &Node, but: Option<&NodeId>) -> Result<(), String> {
480    if Some(&node.id) != but && scene.ids.contains(&node.id) {
481        return Err(format!("{} is in the scene already", node.id));
482    }
483    let mut seen = HashSet::new();
484    fn walk<'a>(n: &'a Node, scene: &Scene, seen: &mut HashSet<&'a NodeId>) -> Result<(), String> {
485        if !seen.insert(&n.id) {
486            return Err(format!("{} is in the subtree twice", n.id));
487        }
488        for c in &n.children {
489            if scene.ids.contains(&c.id) {
490                return Err(format!("{} is in the scene already", c.id));
491            }
492            walk(c, scene, seen)?;
493        }
494        Ok(())
495    }
496    walk(node, scene, &mut seen)
497}
498
499/// Places `node` under the parent at `parent`, after its child `after` or
500/// first, indexing its subtree; returns where it is.
501fn place(scene: &mut Scene, parent: &Order, after: Option<&OrderKey>, node: Node) -> Order {
502    let p = scene.at_mut(parent).expect("indexed");
503    let key = p.children.insert_after(after, node);
504    let mut order = parent.clone();
505    order.push(key.clone());
506    index(scene, &order);
507    order
508}
509
510/// Indexes the subtree at `order`.
511fn index(scene: &mut Scene, order: &Order) {
512    let mut at = &scene.root;
513    for k in order {
514        at = at.children.get(k).expect("placed");
515    }
516    scene.ids.add(at, order.clone());
517}
518
519/// Takes the subtree at `order` out of the scene and its index.
520fn take(scene: &mut Scene, order: &Order) -> Node {
521    let (parent, key) = (&order[..order.len() - 1], &order[order.len() - 1]);
522    let node = scene
523        .at_mut(parent)
524        .expect("indexed")
525        .children
526        .remove(key)
527        .expect("indexed");
528    scene.ids.remove(&node);
529    node
530}
531
532fn revert(scene: &mut Scene, uses: &mut Uses, u: Undo) {
533    match u {
534        Undo::Own(order, mut own) => {
535            let place = scene.at_mut(&order).expect("done");
536            std::mem::swap(&mut place.children, &mut own.children);
537            let new = std::mem::replace(place, own);
538            uses.take_own(&new);
539            uses.add_own(scene.at(&order).expect("done"));
540        }
541        Undo::Take(order) => {
542            let node = take(scene, &order);
543            uses.take(&node);
544        }
545        Undo::Put(parent, key, node) => {
546            uses.add(&node);
547            let p = scene.at_mut(&parent).expect("done");
548            p.children.put(key.clone(), node);
549            let mut order = parent;
550            order.push(key);
551            index(scene, &order);
552        }
553        Undo::Back(now, parent, key) => {
554            let node = take(scene, &now);
555            let p = scene.at_mut(&parent).expect("done");
556            p.children.put(key.clone(), node);
557            let mut order = parent;
558            order.push(key);
559            index(scene, &order);
560        }
561        Undo::Root(old) => {
562            let new = std::mem::replace(&mut scene.root, old);
563            uses.take(&new);
564            uses.add(&scene.root);
565            scene.ids = crate::tree::Ids::of(&scene.root);
566        }
567    }
568}
569
570#[cfg(test)]
571mod tests;