From 4ec3bcee487c1567bc6551f81d4f69eee4379076 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 19 Sep 2022 11:14:58 +0200 Subject: Remove pins and memoization --- src/model/content.rs | 38 +---- src/model/layout.rs | 50 ++----- src/model/locate.rs | 397 --------------------------------------------------- src/model/mod.rs | 2 - src/model/styles.rs | 2 - 5 files changed, 12 insertions(+), 477 deletions(-) delete mode 100644 src/model/locate.rs (limited to 'src/model') diff --git a/src/model/content.rs b/src/model/content.rs index 264785ec..efbaed0e 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -7,8 +7,8 @@ use std::ops::{Add, AddAssign}; use typed_arena::Arena; use super::{ - Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, LocateNode, - Property, Show, ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target, + Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Property, Show, + ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target, }; use crate::diag::StrResult; use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing}; @@ -23,30 +23,6 @@ use crate::util::EcoString; /// /// Relayouts until all pinned locations are converged. pub fn layout(ctx: &mut Context, content: &Content) -> TypResult> { - let mut pass = 0; - let mut frames; - - loop { - let prev = ctx.pins.clone(); - let result = layout_once(ctx, content); - ctx.pins.reset(); - frames = result?; - pass += 1; - - ctx.pins.locate(&frames); - - // Quit if we're done or if we've had five passes. - let unresolved = ctx.pins.unresolved(&prev); - if unresolved == 0 || pass >= 5 { - break; - } - } - - Ok(frames) -} - -/// Layout content into a collection of pages once. -fn layout_once(ctx: &mut Context, content: &Content) -> TypResult> { let copy = ctx.config.styles.clone(); let styles = StyleChain::with_root(©); let scratch = Scratch::default(); @@ -114,8 +90,6 @@ pub enum Content { /// A node that can be realized with styles, optionally with attached /// properties. Show(ShowNode, Option), - /// A node that can be realized with its location on the page. - Locate(LocateNode), /// A pin identified by index. Pin(usize), /// Content with attached styles. @@ -307,7 +281,6 @@ impl Debug for Content { Self::Pagebreak { weak } => write!(f, "Pagebreak({weak})"), Self::Page(page) => page.fmt(f), Self::Show(node, _) => node.fmt(f), - Self::Locate(node) => node.fmt(f), Self::Pin(idx) => write!(f, "Pin({idx})"), Self::Styled(styled) => { let (sub, map) = styled.as_ref(); @@ -425,7 +398,6 @@ impl<'a, 'ctx> Builder<'a, 'ctx> { } Content::Show(node, _) => return self.show(node, styles), - Content::Locate(node) => return self.locate(node, styles), Content::Styled(styled) => return self.styled(styled, styles), Content::Sequence(seq) => return self.sequence(seq, styles), @@ -474,12 +446,6 @@ impl<'a, 'ctx> Builder<'a, 'ctx> { Ok(()) } - fn locate(&mut self, node: &LocateNode, styles: StyleChain<'a>) -> TypResult<()> { - let realized = node.realize(self.ctx)?; - let stored = self.scratch.templates.alloc(realized); - self.accept(stored, styles) - } - fn styled( &mut self, (content, map): &'a (Content, StyleMap), diff --git a/src/model/layout.rs b/src/model/layout.rs index ffe41725..d712a178 100644 --- a/src/model/layout.rs +++ b/src/model/layout.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::hash::Hash; use std::sync::Arc; -use super::{Barrier, NodeId, PinConstraint, Resolve, StyleChain, StyleEntry}; +use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry}; use crate::diag::TypResult; use crate::eval::{RawAlign, RawLength}; use crate::frame::{Element, Frame}; @@ -131,8 +131,6 @@ impl Regions { } } -impl_track_hash!(Regions); - /// A type-erased layouting node with a precomputed hash. #[derive(Clone, Hash)] pub struct LayoutNode(Arc>); @@ -222,43 +220,17 @@ impl Layout for LayoutNode { regions: &Regions, styles: StyleChain, ) -> TypResult> { - let prev = ctx.pins.dirty.replace(false); - - let (result, at, fresh, dirty) = crate::memo::memoized( - (self, &mut *ctx, regions, styles), - |(node, ctx, regions, styles)| { - let hash = fxhash::hash64(&ctx.pins); - let at = ctx.pins.cursor(); - - let barrier = StyleEntry::Barrier(Barrier::new(node.id())); - let styles = barrier.chain(&styles); - - let mut result = node.0.layout(ctx, regions, styles); - if let Some(role) = styles.role() { - result = result.map(|mut frames| { - for frame in frames.iter_mut() { - frame.apply_role(role); - } - frames - }); - } - - let fresh = ctx.pins.from(at); - let dirty = ctx.pins.dirty.get(); - let constraint = PinConstraint(dirty.then(|| hash)); - ((result, at, fresh, dirty), ((), constraint, (), ())) - }, - ); - - ctx.pins.dirty.replace(prev || dirty); - - // Replay the side effect in case of caching. This should currently be - // more or less the only relevant side effect on the context. - if dirty { - ctx.pins.replay(at, fresh); + let barrier = StyleEntry::Barrier(Barrier::new(self.id())); + let styles = barrier.chain(&styles); + + let mut frames = self.0.layout(ctx, regions, styles)?; + if let Some(role) = styles.role() { + for frame in &mut frames { + frame.apply_role(role); + } } - result + Ok(frames) } fn pack(self) -> LayoutNode { @@ -266,8 +238,6 @@ impl Layout for LayoutNode { } } -impl_track_hash!(LayoutNode); - impl Default for LayoutNode { fn default() -> Self { EmptyNode.pack() diff --git a/src/model/locate.rs b/src/model/locate.rs deleted file mode 100644 index d5c71423..00000000 --- a/src/model/locate.rs +++ /dev/null @@ -1,397 +0,0 @@ -use std::cell::Cell; -use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::num::NonZeroUsize; -use std::sync::Arc; - -use super::Content; -use crate::diag::TypResult; -use crate::eval::{Args, Array, Dict, Func, Value}; -use crate::frame::{Element, Frame, Location}; -use crate::geom::{Point, Transform}; -use crate::memo::Track; -use crate::syntax::Spanned; -use crate::util::EcoString; -use crate::Context; - -/// A group of locatable elements. -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct Group(EcoString); - -impl Group { - /// Create a group of elements that is identified by a string key. - pub fn new(key: EcoString) -> Self { - Self(key) - } - - /// Add an entry to the group. - pub fn entry(&self, recipe: Spanned, value: Option) -> LocateNode { - LocateNode::entry(self.clone(), recipe, value) - } - - /// Do something with all entries of a group. - pub fn all(&self, recipe: Spanned) -> LocateNode { - LocateNode::all(self.clone(), recipe) - } -} - -impl Debug for Group { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "group({:?})", self.0) - } -} - -/// A node that can be realized with pinned document locations. -#[derive(Debug, Clone, PartialEq, Hash)] -pub struct LocateNode(Arc); - -impl LocateNode { - /// Create a new locatable single node. - pub fn single(recipe: Spanned) -> Self { - Self(Arc::new(Repr::Single(SingleNode(recipe)))) - } - - /// Create a new locatable group entry node. - pub fn entry(group: Group, recipe: Spanned, value: Option) -> Self { - Self(Arc::new(Repr::Entry(EntryNode { group, recipe, value }))) - } - - /// Create a new node with access to all of a group's members. - pub fn all(group: Group, recipe: Spanned) -> Self { - Self(Arc::new(Repr::All(AllNode { group, recipe }))) - } - - /// Realize the node. - pub fn realize(&self, ctx: &mut Context) -> TypResult { - match self.0.as_ref() { - Repr::Single(single) => single.realize(ctx), - Repr::Entry(entry) => entry.realize(ctx), - Repr::All(all) => all.realize(ctx), - } - } -} - -/// The different kinds of locate nodes. -#[derive(Debug, Clone, PartialEq, Hash)] -enum Repr { - /// A single `locate(me => ...)`. - Single(SingleNode), - /// A locatable group entry. - Entry(EntryNode), - /// A recipe for all entries of a group. - All(AllNode), -} - -/// An ungrouped locatable node. -#[derive(Debug, Clone, PartialEq, Hash)] -struct SingleNode(Spanned); - -impl SingleNode { - fn realize(&self, ctx: &mut Context) -> TypResult { - let idx = ctx.pins.cursor; - let pin = ctx.pins.get_or_create(None, None); - let dict = pin.encode(None); - let args = Args::new(self.0.span, [Value::Dict(dict)]); - Ok(Content::Pin(idx) + self.0.v.call_detached(ctx, args)?.display()) - } -} - -/// A locatable grouped node which can interact with its peers' details. -#[derive(Debug, Clone, PartialEq, Hash)] -struct EntryNode { - /// Which group the node belongs to. - group: Group, - /// The recipe to execute. - recipe: Spanned, - /// An arbitrary attached value. - value: Option, -} - -impl EntryNode { - fn realize(&self, ctx: &mut Context) -> TypResult { - let idx = ctx.pins.cursor; - let pin = ctx.pins.get_or_create(Some(self.group.clone()), self.value.clone()); - - // Determine the index among the peers. - let index = ctx - .pins - .iter() - .enumerate() - .filter(|&(k, other)| { - other.is_in(&self.group) - && if k < idx { - other.flow <= pin.flow - } else { - other.flow < pin.flow - } - }) - .count(); - - // Prepare first argument. - let dict = pin.encode(Some(index)); - let mut args = Args::new(self.recipe.span, [Value::Dict(dict)]); - - // Collect all group members if second argument is requested. - if self.recipe.v.argc() == Some(2) { - let all = ctx.pins.encode_group(&self.group); - args.push(self.recipe.span, Value::Array(all)) - } - - Ok(Content::Pin(idx) + self.recipe.v.call_detached(ctx, args)?.display()) - } -} - -/// A node with access to a group's members. -#[derive(Debug, Clone, PartialEq, Hash)] -struct AllNode { - /// Which group the node has access to. - group: Group, - /// The recipe to execute. - recipe: Spanned, -} - -impl AllNode { - fn realize(&self, ctx: &mut Context) -> TypResult { - let all = ctx.pins.encode_group(&self.group); - let args = Args::new(self.recipe.span, [Value::Array(all)]); - Ok(self.recipe.v.call_detached(ctx, args)?.display()) - } -} - -/// Manages document pins. -#[derive(Debug, Clone)] -pub struct PinBoard { - /// All currently active pins. - list: Vec, - /// The index of the next pin, in order. - cursor: usize, - /// If larger than zero, the board is frozen and the cursor will not be - /// advanced. This is used to disable pinning during measure-only layouting. - frozen: usize, - /// Whether the board was accessed. - pub(super) dirty: Cell, -} - -impl PinBoard { - /// Create an empty pin board. - pub fn new() -> Self { - Self { - list: vec![], - cursor: 0, - frozen: 0, - dirty: Cell::new(false), - } - } -} - -/// Internal methods for implementation of locatable nodes. -impl PinBoard { - /// Access or create the next pin. - fn get_or_create(&mut self, group: Option, value: Option) -> Pin { - self.dirty.set(true); - if self.frozen() { - return Pin::default(); - } - - let cursor = self.cursor; - self.cursor += 1; - if self.cursor >= self.list.len() { - self.list.resize(self.cursor, Pin::default()); - } - - let pin = &mut self.list[cursor]; - pin.group = group; - pin.value = value; - pin.clone() - } - - /// Encode a group into a user-facing array. - fn encode_group(&self, group: &Group) -> Array { - self.dirty.set(true); - let mut all: Vec<_> = self.iter().filter(|pin| pin.is_in(group)).collect(); - all.sort_by_key(|pin| pin.flow); - all.iter() - .enumerate() - .map(|(index, member)| Value::Dict(member.encode(Some(index)))) - .collect() - } - - /// Iterate over all pins on the board. - fn iter(&self) -> std::slice::Iter { - self.dirty.set(true); - self.list.iter() - } -} - -/// Caching related methods. -impl PinBoard { - /// The current cursor. - pub fn cursor(&self) -> usize { - self.cursor - } - - /// All pins from `prev` to the current cursor. - pub fn from(&self, prev: usize) -> Vec { - self.list[prev .. self.cursor].to_vec() - } - - /// Add the given pins at the given location and set the cursor behind them. - pub fn replay(&mut self, at: usize, pins: Vec) { - if !self.frozen() { - self.cursor = at + pins.len(); - let end = self.cursor.min(self.list.len()); - self.list.splice(at .. end, pins); - } - } -} - -/// Control methods that are called during layout. -impl PinBoard { - /// Freeze the board to prevent modifications. - pub fn freeze(&mut self) { - self.frozen += 1; - } - - /// Freeze the board to prevent modifications. - pub fn unfreeze(&mut self) { - self.frozen -= 1; - } - - /// Whether the board is currently frozen. - pub fn frozen(&self) -> bool { - self.frozen > 0 - } -} - -/// Methods that are called in between layout passes. -impl PinBoard { - /// Reset the cursor and remove all unused pins. - pub fn reset(&mut self) { - self.list.truncate(self.cursor); - self.cursor = 0; - self.dirty.set(false); - } - - /// Locate all pins in the frames. - pub fn locate(&mut self, frames: &[Frame]) { - let mut flow = 0; - for (i, frame) in frames.iter().enumerate() { - locate_in_frame( - &mut self.list, - &mut flow, - NonZeroUsize::new(1 + i).unwrap(), - frame, - Transform::identity(), - ); - } - } - - /// How many pins are unresolved in comparison to an earlier snapshot. - pub fn unresolved(&self, prev: &Self) -> usize { - self.list.len() - self.list.iter().zip(&prev.list).filter(|(a, b)| a == b).count() - } -} - -/// Locate all pins in a frame. -fn locate_in_frame( - pins: &mut [Pin], - flow: &mut usize, - page: NonZeroUsize, - frame: &Frame, - ts: Transform, -) { - for &(pos, ref element) in frame.elements() { - match element { - Element::Group(group) => { - let ts = ts - .pre_concat(Transform::translate(pos.x, pos.y)) - .pre_concat(group.transform); - locate_in_frame(pins, flow, page, &group.frame, ts); - } - - Element::Pin(idx) => { - let pin = &mut pins[*idx]; - pin.loc.page = page; - pin.loc.pos = pos.transform(ts); - pin.flow = *flow; - *flow += 1; - } - - _ => {} - } - } -} - -impl Hash for PinBoard { - fn hash(&self, state: &mut H) { - self.list.hash(state); - self.cursor.hash(state); - self.frozen.hash(state); - } -} - -/// Describes pin usage. -#[derive(Debug, Copy, Clone)] -pub struct PinConstraint(pub Option); - -impl Track for PinBoard { - type Constraint = PinConstraint; - - fn key(&self, _: &mut H) {} - - fn matches(&self, constraint: &Self::Constraint) -> bool { - match constraint.0 { - Some(hash) => fxhash::hash64(self) == hash, - None => true, - } - } -} - -/// A document pin. -#[derive(Debug, Clone, PartialEq, Hash)] -pub struct Pin { - /// The physical location of the pin in the document. - loc: Location, - /// The flow index. - flow: usize, - /// The group the pin belongs to, if any. - group: Option, - /// An arbitrary attached value. - value: Option, -} - -impl Pin { - /// Whether the pin is part of the given group. - fn is_in(&self, group: &Group) -> bool { - self.group.as_ref() == Some(group) - } - - /// Encode into a user-facing dictionary. - fn encode(&self, index: Option) -> Dict { - let mut dict = self.loc.encode(); - - if let Some(value) = &self.value { - dict.insert("value".into(), value.clone()); - } - - if let Some(index) = index { - dict.insert("index".into(), Value::Int(index as i64)); - } - - dict - } -} - -impl Default for Pin { - fn default() -> Self { - Self { - loc: Location { - page: NonZeroUsize::new(1).unwrap(), - pos: Point::zero(), - }, - flow: 0, - group: None, - value: None, - } - } -} diff --git a/src/model/mod.rs b/src/model/mod.rs index 379b633f..5c8b82c0 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -5,7 +5,6 @@ mod styles; mod collapse; mod content; mod layout; -mod locate; mod property; mod recipe; mod show; @@ -13,7 +12,6 @@ mod show; pub use collapse::*; pub use content::*; pub use layout::*; -pub use locate::*; pub use property::*; pub use recipe::*; pub use show::*; diff --git a/src/model/styles.rs b/src/model/styles.rs index 03cdcaac..eab33402 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -413,8 +413,6 @@ impl PartialEq for StyleChain<'_> { } } -impl_track_hash!(StyleChain<'_>); - /// An iterator over the values in a style chain. struct Values<'a, K> { entries: Entries<'a>, -- cgit v1.2.3