Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_austenite/src/delta.rs

4.8 KiB, 1 run

created by r1870400018:58772, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! The changed-only page delta, for the browser live view.
2//!
3//! Daimond's live view rerenders on every keystroke. Returning the full set of per-page SVG strings each
4//! compile costs the consumer a fresh parse of every page and costs the wasm heap the memory of the whole
5//! rendered document -- the very memory the Austenite swap exists to save. This layer returns instead only
6//! the pages whose rendered SVG has changed since the consumer's last-known set, keyed by a stable page
7//! identity so the consumer caches the rest:
8//!
9//! ```text
10//! { version, order: [id, ...], changed: [{ id, svg }, ...], reset }
11//! ```
12//!
13//! * `order` is the full per-compile sequence of page ids. The consumer renders in this order, keeping a
14//! cache keyed by id and dropping any id absent from `order`.
15//! * `changed` carries the SVG of only those pages whose id is new this compile (not in the consumer's
16//! prior set), so a page the consumer already holds is never resent.
17//! * `version` is a monotonic counter, stepped each compile, for the consumer to order deltas by.
18//! * `reset` is true on the first compile and any compile made against an empty prior set: `changed` then
19//! carries every page in `order`, since the consumer holds nothing to reuse.
20//!
21//! **The identity is a content hash of the WHOLE page frame** -- body and furniture (running head, folio)
22//! alike -- so two compiles yield the same id for a page exactly when it renders to the same SVG bytes.
23//! This is deliberately keyed on *content*, not on the page's ordinal position: inserting a page near the
24//! front shifts every later page's position but leaves each later page's content-hash unchanged, so only
25//! the inserted page is resent. A position-keyed cache would resend every page from the insertion point on,
26//! because each would fall at a slot that last held a different page. It is a superset of the emit memo's
27//! [`page_key`](crate::emit::svg), which hashes the body alone because the memo always redraws the
28//! furniture fresh; the delta needs the folio in the key, since the consumer reuses the whole SVG by id and
29//! a page whose only change is its printed folio genuinely renders differently.
30//!
31//! **Residency.** The prior id set is supplied by the consumer on each compile (its `known` set, the ids it
32//! still holds in its own cache) and returned as the new `order`; the compiler retains none of it between
33//! compiles. The rendered SVG of a page is emitted into `changed` when its id is new and then dropped, never
34//! retained. So the wasm heap holds roughly one page's SVG plus one id per page during a compile, and
35//! nothing between compiles -- not the whole rendered document, and not the prior set. Keeping the prior set
36//! in the compiler would also be wrong, not merely heavier: a consumer that cleared its cache while the one
37//! singleton compiler lived on would be told nothing changed against a cache holding nothing.
38
39use crate::emit::svg;
40use crate::page::Page;
41
42use oxedyne_fe2o3_core::prelude::*;
43
44use std::collections::HashSet;
45
46/// A changed-only compile result: the per-compile page-id sequence, the SVG of only the pages whose id is
47/// new since the prior set, the monotonic version and whether this was a full (reset) send.
48pub struct PageDelta {
49 pub version: u32,
50 pub order: Vec<u64>, // every page's content id, in reading order
51 pub changed: Vec<(u64, String)>, // (id, svg) for each id new this compile, rendered once
52 pub reset: bool, // true when the prior set was empty, so `changed` carries all of `order`
53}
54
55/// Computes the delta between this compile's `pages` and the consumer's `prior` id set (its last `order`,
56/// empty on the first compile). Each page's id is its whole-frame content hash; a page whose id is not in
57/// `prior` is rendered and carried in `changed`, a page whose id is already known is left for the consumer
58/// to reuse. An id is rendered at most once per compile, so two byte-identical pages (which share an id)
59/// send one SVG and appear twice in `order` -- the consumer resolves both slots to the one cached page.
60///
61/// `prior_version` is the version the caller last returned; the result steps it by one.
62pub fn compute(pages: &[Page], prior: &[u64], prior_version: u32) -> Outcome<PageDelta> {
63 let reset = prior.is_empty();
64 let known: HashSet<u64> = prior.iter().copied().collect();
65 let mut order = Vec::with_capacity(pages.len());
66 let mut changed = Vec::new();
67 // The ids rendered so far this compile, so a repeated page is emitted once though `order` names it twice.
68 let mut sent: HashSet<u64> = HashSet::new();
69 for page in pages {
70 let id = svg::page_id(page);
71 order.push(id);
72 if !known.contains(&id) && sent.insert(id) {
73 let rendered = res!(svg::render_page(page));
74 changed.push((id, rendered));
75 }
76 }
77 Ok(PageDelta {
78 version: prior_version.wrapping_add(1),
79 order,
80 changed,
81 reset,
82 })
83}