use std::collections::{hash_map::Entry, HashMap}; use std::ops::Deref; use super::*; /// Caches layouting artifacts. #[derive(Default, Debug, Clone)] pub struct LayoutCache { /// Maps from node hashes to the resulting frames and regions in which the /// frames are valid. The right hand side of the hash map is a vector of /// results because across one or more compilations, multiple different /// layouts of the same node may have been requested. pub frames: HashMap>, /// In how many compilations this cache has been used. age: usize, } impl LayoutCache { /// Create a new, empty layout cache. pub fn new() -> Self { Self { frames: HashMap::new(), age: 0 } } /// Clear the cache. pub fn clear(&mut self) { self.frames.clear(); } /// Amount of items in the cache. pub fn len(&self) -> usize { self.frames.iter().map(|(_, e)| e.len()).sum() } /// Retains all elements for which the closure on the level returns `true`. pub fn retain(&mut self, mut f: F) where F: FnMut(usize) -> bool, { for (_, entries) in self.frames.iter_mut() { entries.retain(|entry| f(entry.level)); } } /// Prepare the cache for the next round of compilation pub fn turnaround(&mut self) { self.age += 1; for entry in self.frames.iter_mut().flat_map(|(_, x)| x.iter_mut()) { for i in 0 .. (entry.temperature.len() - 1) { entry.temperature[i + 1] = entry.temperature[i]; } entry.temperature[0] = 0; entry.age += 1; } } /// The amount of levels stored in the cache. pub fn levels(&self) -> usize { self.frames .iter() .flat_map(|(_, x)| x) .map(|entry| entry.level + 1) .max() .unwrap_or(0) } /// Fetches the appropriate entry from the cache if there is any. pub fn get( &mut self, hash: u64, regions: Regions, ) -> Option>>> { self.frames.get_mut(&hash).and_then(|frames| { for frame in frames { let res = frame.check(regions.clone()); if res.is_some() { return res; } } None }) } /// Inserts a new frame set into the cache. pub fn insert( &mut self, hash: u64, frames: Vec>>, level: usize, ) { let entry = FramesEntry::new(frames, level); match self.frames.entry(hash) { Entry::Occupied(o) => o.into_mut().push(entry), Entry::Vacant(v) => { v.insert(vec![entry]); } } } } /// Cached frames from past layouting. #[derive(Debug, Clone)] pub struct FramesEntry { /// The cached frames for a node. pub frames: Vec>>, /// How nested the frame was in the context is was originally appearing in. pub level: usize, /// For how long the element already exists. age: usize, /// How much the element was accessed during the last five compilations, the /// most recent one being the first element. temperature: [usize; 5], } impl FramesEntry { /// Construct a new instance. pub fn new(frames: Vec>>, level: usize) -> Self { Self { frames, level, age: 1, temperature: [0; 5], } } /// Checks if the cached [`Frame`] is valid for the given regions. pub fn check(&mut self, mut regions: Regions) -> Option>>> { for (i, frame) in self.frames.iter().enumerate() { if (i != 0 && !regions.next()) || !frame.constraints.check(®ions) { return None; } } self.temperature[0] += 1; Some(self.frames.clone()) } /// Get the amount of compilation cycles this item has remained in the /// cache. pub fn age(&self) -> usize { self.age } /// Get the amount of consecutive cycles in which this item has not /// been used. pub fn cooldown(&self) -> usize { let mut cycle = 0; for &temp in &self.temperature[.. self.age] { if temp > 0 { return cycle; } cycle += 1; } cycle } /// Whether this element was used in the last compilation cycle. pub fn hit(&self) -> bool { self.temperature[0] != 0 } } /// Describe regions that match them. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Constraints { /// The minimum available length in the region. pub min: Spec>, /// The maximum available length in the region. pub max: Spec>, /// The available length in the region. pub exact: Spec>, /// The base length of the region used for relative length resolution. pub base: Spec>, /// The expand settings of the region. pub expand: Spec, } impl Constraints { /// Create a new region constraint. pub fn new(expand: Spec) -> Self { Self { min: Spec::default(), max: Spec::default(), exact: Spec::default(), base: Spec::default(), expand, } } /// Set the appropriate base constraints for (relative) width and height /// metrics, respectively. pub fn set_base_using_linears( &mut self, size: Spec>, regions: &Regions, ) { // The full sizes need to be equal if there is a relative component in the sizes. if size.horizontal.map_or(false, |l| l.is_relative()) { self.base.horizontal = Some(regions.base.width); } if size.vertical.map_or(false, |l| l.is_relative()) { self.base.vertical = Some(regions.base.height); } } fn check(&self, regions: &Regions) -> bool { if self.expand != regions.expand { return false; } let base = regions.base.to_spec(); let current = regions.current.to_spec(); current.eq_by(&self.min, |x, y| y.map_or(true, |y| x.fits(y))) && current.eq_by(&self.max, |x, y| y.map_or(true, |y| x < &y)) && current.eq_by(&self.exact, |x, y| y.map_or(true, |y| x.approx_eq(y))) && base.eq_by(&self.base, |x, y| y.map_or(true, |y| x.approx_eq(y))) } /// Changes all constraints by adding the `size` to them if they are `Some`. pub fn mutate(&mut self, size: Size, regions: &Regions) { for spec in std::array::IntoIter::new([ &mut self.min, &mut self.max, &mut self.exact, &mut self.base, ]) { if let Some(horizontal) = spec.horizontal.as_mut() { *horizontal += size.width; } if let Some(vertical) = spec.vertical.as_mut() { *vertical += size.height; } } let current = regions.current.to_spec(); let base = regions.base.to_spec(); self.exact.horizontal.set_if_some(current.horizontal); self.exact.vertical.set_if_some(current.vertical); self.base.horizontal.set_if_some(base.horizontal); self.base.vertical.set_if_some(base.vertical); } } /// Carries an item that only applies to certain regions and the constraints /// that describe these regions. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Constrained { pub item: T, pub constraints: Constraints, } impl Deref for Constrained { type Target = T; fn deref(&self) -> &Self::Target { &self.item } } /// Extends length-related options by providing convenience methods for setting /// minimum and maximum lengths on them, even if they are `None`. pub trait OptionExt { // Sets `other` as the value if the Option is `None` or if it contains a // value larger than `other`. fn set_min(&mut self, other: Length); // Sets `other` as the value if the Option is `None` or if it contains a // value smaller than `other`. fn set_max(&mut self, other: Length); /// Sets `other` as the value if the Option is `Some`. fn set_if_some(&mut self, other: Length); } impl OptionExt for Option { fn set_min(&mut self, other: Length) { match self { Some(x) => x.set_min(other), None => *self = Some(other), } } fn set_max(&mut self, other: Length) { match self { Some(x) => x.set_max(other), None => *self = Some(other), } } fn set_if_some(&mut self, other: Length) { if self.is_some() { *self = Some(other); } } }