Added LazyHash (#3451)

Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
Sébastien d'Herbais de Thun 2024-02-21 09:40:13 +01:00 committed by GitHub
parent be49935753
commit d0dd81cddf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 264 additions and 144 deletions

7
Cargo.lock generated
View file

@ -1747,6 +1747,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "postcard"
version = "1.0.8"
@ -2578,6 +2584,7 @@ dependencies = [
"once_cell",
"palette",
"phf",
"portable-atomic",
"qcms",
"rayon",
"regex",

View file

@ -75,6 +75,7 @@ pathdiff = "0.2"
pdf-writer = "0.9.2"
phf = { version = "0.11", features = ["macros"] }
pixglyph = "0.3"
portable-atomic = "1.6"
proc-macro2 = "1"
pulldown-cmark = "0.9"
quote = "1"

View file

@ -78,7 +78,6 @@ fn retrieve(
.introspector
.query(&selector.0)
.into_iter()
.map(|x| x.into_inner())
.collect::<Vec<_>>())
}

View file

@ -44,6 +44,7 @@ once_cell = { workspace = true }
palette = { workspace = true }
qcms = { workspace = true }
phf = { workspace = true }
portable-atomic = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
roxmltree = { workspace = true }

View file

@ -1,4 +1,4 @@
use comemo::{Prehashed, Tracked, TrackedMut};
use comemo::{Tracked, TrackedMut};
use ecow::{eco_format, EcoVec};
use crate::diag::{bail, error, At, HintedStrResult, SourceResult, Trace, Tracepoint};
@ -14,6 +14,7 @@ use crate::symbols::Symbol;
use crate::syntax::ast::{self, AstNode};
use crate::syntax::{Spanned, SyntaxNode};
use crate::text::TextElem;
use crate::util::LazyHash;
use crate::World;
impl Eval for ast::FuncCall<'_> {
@ -260,7 +261,7 @@ impl Eval for ast::Closure<'_> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn call_closure(
func: &Func,
closure: &Prehashed<Closure>,
closure: &LazyHash<Closure>,
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,

View file

@ -3,12 +3,12 @@ use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, AddAssign, Deref};
use std::sync::Arc;
use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use serde::{Serialize, Serializer};
use crate::diag::{bail, StrResult};
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
use crate::util::LazyHash;
/// A sequence of bytes.
///
@ -40,12 +40,12 @@ use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value
/// ```
#[ty(scope, cast)]
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Bytes(Arc<Prehashed<Cow<'static, [u8]>>>);
pub struct Bytes(Arc<LazyHash<Cow<'static, [u8]>>>);
impl Bytes {
/// Create a buffer from a static byte slice.
pub fn from_static(slice: &'static [u8]) -> Self {
Self(Arc::new(Prehashed::new(Cow::Borrowed(slice))))
Self(Arc::new(LazyHash::new(Cow::Borrowed(slice))))
}
/// Return `true` if the length is 0.
@ -182,13 +182,13 @@ impl AsRef<[u8]> for Bytes {
impl From<&[u8]> for Bytes {
fn from(slice: &[u8]) -> Self {
Self(Arc::new(Prehashed::new(slice.to_vec().into())))
Self(Arc::new(LazyHash::new(slice.to_vec().into())))
}
}
impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Self {
Self(Arc::new(Prehashed::new(vec.into())))
Self(Arc::new(LazyHash::new(vec.into())))
}
}
@ -208,9 +208,7 @@ impl AddAssign for Bytes {
} else if self.is_empty() {
*self = rhs;
} else if Arc::strong_count(&self.0) == 1 && matches!(**self.0, Cow::Owned(_)) {
Arc::make_mut(&mut self.0).update(|cow| {
cow.to_mut().extend_from_slice(&rhs);
})
Arc::make_mut(&mut self.0).to_mut().extend_from_slice(&rhs);
} else {
*self = Self::from([self.as_slice(), rhs.as_slice()].concat());
}

View file

@ -3,7 +3,6 @@ use std::fmt::Write;
use std::hash::Hash;
use std::ops::Add;
use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use smallvec::SmallVec;
use unicode_math_class::MathClass;
@ -98,20 +97,6 @@ impl<T: NativeElement + Reflect> Reflect for Packed<T> {
}
}
impl<T: Reflect> Reflect for Prehashed<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for StrResult<T> {
fn input() -> CastInfo {
T::input()
@ -206,12 +191,6 @@ impl<T: IntoValue> IntoValue for Spanned<T> {
}
}
impl<T: IntoValue + Hash + 'static> IntoValue for Prehashed<T> {
fn into_value(self) -> Value {
self.into_inner().into_value()
}
}
/// Cast a Rust type or result into a [`SourceResult<Value>`].
///
/// Converts `T`, [`StrResult<T>`], or [`SourceResult<T>`] into
@ -278,12 +257,6 @@ impl<T: NativeElement + FromValue> FromValue for Packed<T> {
}
}
impl<T: FromValue + Hash + 'static> FromValue for Prehashed<T> {
fn from_value(value: Value) -> StrResult<Self> {
Ok(Self::new(T::from_value(value)?))
}
}
impl<T: FromValue> FromValue<Spanned<Value>> for T {
fn from_value(value: Spanned<Value>) -> StrResult<Self> {
T::from_value(value.v)

View file

@ -6,7 +6,6 @@ use std::marker::PhantomData;
use std::ops::{Add, AddAssign, Deref, DerefMut};
use std::sync::Arc;
use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use serde::{Serialize, Serializer};
use smallvec::smallvec;
@ -23,7 +22,7 @@ use crate::model::{Destination, EmphElem, StrongElem};
use crate::realize::{Behave, Behaviour};
use crate::syntax::Span;
use crate::text::UnderlineElem;
use crate::util::{fat, BitSet};
use crate::util::{fat, BitSet, LazyHash};
/// A piece of document content.
///
@ -80,7 +79,7 @@ pub struct Content {
/// The inner representation behind the `Arc`.
#[derive(Hash)]
struct Inner<T: ?Sized> {
struct Inner<T: ?Sized + 'static> {
/// An optional label attached to the element.
label: Option<Label>,
/// The element's location which identifies it in the layouted output.
@ -91,7 +90,7 @@ struct Inner<T: ?Sized> {
/// recipe from the top of the style chain (counting from 1).
lifecycle: BitSet,
/// The element's raw data.
elem: T,
elem: LazyHash<T>,
}
impl Content {
@ -102,7 +101,7 @@ impl Content {
label: None,
location: None,
lifecycle: BitSet::new(),
elem,
elem: elem.into(),
}),
span: Span::detached(),
}
@ -235,9 +234,9 @@ impl Content {
let Some(first) = iter.next() else { return Self::empty() };
let Some(second) = iter.next() else { return first };
SequenceElem::new(
std::iter::once(Prehashed::new(first))
.chain(std::iter::once(Prehashed::new(second)))
.chain(iter.map(Prehashed::new))
std::iter::once(first)
.chain(std::iter::once(second))
.chain(iter)
.collect(),
)
.into()
@ -379,7 +378,7 @@ impl Content {
style_elem.styles.apply(styles);
self
} else {
StyledElem::new(Prehashed::new(self), styles).into()
StyledElem::new(self, styles).into()
}
}
@ -620,11 +619,11 @@ impl Add for Content {
lhs
}
(Some(seq_lhs), None) => {
seq_lhs.children.push(Prehashed::new(rhs));
seq_lhs.children.push(rhs);
lhs
}
(None, Some(rhs_seq)) => {
rhs_seq.children.insert(0, Prehashed::new(lhs));
rhs_seq.children.insert(0, lhs);
rhs
}
(None, None) => Self::sequence([lhs, rhs]),
@ -643,15 +642,12 @@ impl<'a> Add<&'a Self> for Content {
lhs
}
(Some(seq_lhs), None) => {
seq_lhs.children.push(Prehashed::new(rhs.clone()));
seq_lhs.children.push(rhs.clone());
lhs
}
(None, Some(_)) => {
let mut rhs = rhs.clone();
rhs.to_packed_mut::<SequenceElem>()
.unwrap()
.children
.insert(0, Prehashed::new(lhs));
rhs.to_packed_mut::<SequenceElem>().unwrap().children.insert(0, lhs);
rhs
}
(None, None) => Self::sequence([lhs, rhs.clone()]),
@ -713,7 +709,7 @@ impl<T: NativeElement> Bounds for T {
label: inner.label,
location: inner.location,
lifecycle: inner.lifecycle.clone(),
elem: self.clone(),
elem: LazyHash::with_hash(self.clone(), inner.elem.hash()),
}),
span,
}
@ -845,7 +841,7 @@ impl<T: NativeElement> Deref for Packed<T> {
// an element of type `T`.
// - This downcast works the same way as dyn Any's does. We can't reuse
// that one because we don't want to pay the cost for every deref.
let elem = &self.0.inner.elem;
let elem = &*self.0.inner.elem;
unsafe { &*(elem as *const dyn Bounds as *const T) }
}
}
@ -858,7 +854,7 @@ impl<T: NativeElement> DerefMut for Packed<T> {
// - We have guaranteed unique access thanks to `make_mut`.
// - This downcast works the same way as dyn Any's does. We can't reuse
// that one because we don't want to pay the cost for every deref.
let elem = &mut self.0.make_mut().elem;
let elem = &mut *self.0.make_mut().elem;
unsafe { &mut *(elem as *mut dyn Bounds as *mut T) }
}
}
@ -874,7 +870,7 @@ impl<T: NativeElement + Debug> Debug for Packed<T> {
pub struct SequenceElem {
/// The elements.
#[required]
pub children: Vec<Prehashed<Content>>,
pub children: Vec<Content>,
}
impl Debug for SequenceElem {
@ -894,10 +890,7 @@ impl Default for SequenceElem {
impl PartialEq for SequenceElem {
fn eq(&self, other: &Self) -> bool {
self.children
.iter()
.map(|c| &**c)
.eq(other.children.iter().map(|c| &**c))
self.children.iter().eq(other.children.iter())
}
}
@ -926,7 +919,7 @@ impl Repr for SequenceElem {
pub struct StyledElem {
/// The content.
#[required]
pub child: Prehashed<Content>,
pub child: Content,
/// The styles.
#[required]
pub styles: Styles,
@ -943,7 +936,7 @@ impl Debug for StyledElem {
impl PartialEq for StyledElem {
fn eq(&self, other: &Self) -> bool {
*self.child == *other.child
self.child == other.child
}
}

View file

@ -1,7 +1,7 @@
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use comemo::{Prehashed, TrackedMut};
use comemo::TrackedMut;
use ecow::{eco_format, EcoString};
use once_cell::sync::Lazy;
@ -12,7 +12,7 @@ use crate::foundations::{
Type, Value,
};
use crate::syntax::{ast, Span, SyntaxNode};
use crate::util::Static;
use crate::util::{LazyHash, Static};
#[doc(inline)]
pub use typst_macros::func;
@ -141,7 +141,7 @@ enum Repr {
/// A function for an element.
Element(Element),
/// A user-defined closure.
Closure(Arc<Prehashed<Closure>>),
Closure(Arc<LazyHash<Closure>>),
/// A nested function with pre-applied arguments.
With(Arc<(Func, Args)>),
}
@ -485,7 +485,7 @@ impl Closure {
impl From<Closure> for Func {
fn from(closure: Closure) -> Self {
Repr::Closure(Arc::new(Prehashed::new(closure))).into()
Repr::Closure(Arc::new(LazyHash::new(closure))).into()
}
}

View file

@ -3,7 +3,6 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::{mem, ptr};
use comemo::Prehashed;
use ecow::{eco_vec, EcoString, EcoVec};
use smallvec::SmallVec;
@ -15,6 +14,7 @@ use crate::foundations::{
};
use crate::syntax::Span;
use crate::text::{FontFamily, FontList, TextElem};
use crate::util::LazyHash;
/// Provides access to active styles.
///
@ -65,7 +65,7 @@ impl Show for Packed<StyleElem> {
/// A list of style properties.
#[ty(cast)]
#[derive(Default, PartialEq, Clone, Hash)]
pub struct Styles(EcoVec<Prehashed<Style>>);
pub struct Styles(EcoVec<LazyHash<Style>>);
impl Styles {
/// Create a new, empty style list.
@ -89,7 +89,7 @@ impl Styles {
/// style map, `self` contributes the outer values and `value` is the inner
/// one.
pub fn set(&mut self, style: impl Into<Style>) {
self.0.push(Prehashed::new(style.into()));
self.0.push(LazyHash::new(style.into()));
}
/// Remove the style that was last set.
@ -105,22 +105,20 @@ impl Styles {
/// Apply one outer styles.
pub fn apply_one(&mut self, outer: Style) {
self.0.insert(0, Prehashed::new(outer));
self.0.insert(0, LazyHash::new(outer));
}
/// Apply a slice of outer styles.
pub fn apply_slice(&mut self, outer: &[Prehashed<Style>]) {
pub fn apply_slice(&mut self, outer: &[LazyHash<Style>]) {
self.0 = outer.iter().cloned().chain(mem::take(self).0).collect();
}
/// Add an origin span to all contained properties.
pub fn spanned(mut self, span: Span) -> Self {
for entry in self.0.make_mut() {
entry.update(|entry| {
if let Style::Property(property) = entry {
property.span = Some(span);
}
});
if let Style::Property(property) = &mut **entry {
property.span = Some(span);
}
}
self
}
@ -147,15 +145,15 @@ impl Styles {
}
}
impl From<Prehashed<Style>> for Styles {
fn from(style: Prehashed<Style>) -> Self {
impl From<LazyHash<Style>> for Styles {
fn from(style: LazyHash<Style>) -> Self {
Self(eco_vec![style])
}
}
impl From<Style> for Styles {
fn from(style: Style) -> Self {
Self(eco_vec![Prehashed::new(style)])
Self(eco_vec![LazyHash::new(style)])
}
}
@ -262,8 +260,8 @@ impl Property {
}
/// Turn this property into prehashed style.
pub fn wrap(self) -> Prehashed<Style> {
Prehashed::new(Style::Property(self))
pub fn wrap(self) -> LazyHash<Style> {
LazyHash::new(Style::Property(self))
}
}
@ -457,7 +455,7 @@ cast! {
#[derive(Default, Clone, Copy, Hash)]
pub struct StyleChain<'a> {
/// The first link of this chain.
head: &'a [Prehashed<Style>],
head: &'a [LazyHash<Style>],
/// The remaining links in the chain.
tail: Option<&'a Self>,
}
@ -616,7 +614,7 @@ pub trait Chainable {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a>;
}
impl Chainable for Prehashed<Style> {
impl Chainable for LazyHash<Style> {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
StyleChain {
head: std::slice::from_ref(self),
@ -625,7 +623,7 @@ impl Chainable for Prehashed<Style> {
}
}
impl Chainable for [Prehashed<Style>] {
impl Chainable for [LazyHash<Style>] {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
if self.is_empty() {
*outer
@ -635,7 +633,7 @@ impl Chainable for [Prehashed<Style>] {
}
}
impl<const N: usize> Chainable for [Prehashed<Style>; N] {
impl<const N: usize> Chainable for [LazyHash<Style>; N] {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
Chainable::chain(self.as_slice(), outer)
}
@ -649,7 +647,7 @@ impl Chainable for Styles {
/// An iterator over the entries in a style chain.
pub struct Entries<'a> {
inner: std::slice::Iter<'a, Prehashed<Style>>,
inner: std::slice::Iter<'a, LazyHash<Style>>,
links: Links<'a>,
}
@ -674,7 +672,7 @@ impl<'a> Iterator for Entries<'a> {
pub struct Links<'a>(Option<StyleChain<'a>>);
impl<'a> Iterator for Links<'a> {
type Item = &'a [Prehashed<Style>];
type Item = &'a [LazyHash<Style>];
fn next(&mut self) -> Option<Self::Item> {
let StyleChain { head, tail } = self.0?;

View file

@ -4,7 +4,6 @@ use std::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::RwLock;
use comemo::Prehashed;
use ecow::{eco_format, EcoVec};
use indexmap::IndexMap;
use smallvec::SmallVec;
@ -22,7 +21,7 @@ pub struct Introspector {
/// The number of pages in the document.
pages: usize,
/// All introspectable elements.
elems: IndexMap<Location, (Prehashed<Content>, Position)>,
elems: IndexMap<Location, (Content, Position)>,
/// Maps labels to their indices in the element list. We use a smallvec such
/// that if the label is unique, we don't need to allocate.
labels: HashMap<Label, SmallVec<[usize; 1]>>,
@ -66,7 +65,6 @@ impl Introspector {
if !self.elems.contains_key(&content.location().unwrap()) =>
{
let pos = pos.transform(ts);
let content = Prehashed::new(content.clone());
let ret = self.elems.insert(
content.location().unwrap(),
(content.clone(), Position { page, point: pos }),
@ -84,12 +82,12 @@ impl Introspector {
}
/// Iterate over all locatable elements.
pub fn all(&self) -> impl Iterator<Item = &Prehashed<Content>> + '_ {
pub fn all(&self) -> impl Iterator<Item = &Content> + '_ {
self.elems.values().map(|(c, _)| c)
}
/// Get an element by its location.
fn get(&self, location: &Location) -> Option<&Prehashed<Content>> {
fn get(&self, location: &Location) -> Option<&Content> {
self.elems.get(location).map(|(elem, _)| elem)
}
@ -101,11 +99,7 @@ impl Introspector {
}
/// Perform a binary search for `elem` among the `list`.
fn binary_search(
&self,
list: &[Prehashed<Content>],
elem: &Content,
) -> Result<usize, usize> {
fn binary_search(&self, list: &[Content], elem: &Content) -> Result<usize, usize> {
list.binary_search_by_key(&self.index(elem), |elem| self.index(elem))
}
}
@ -113,7 +107,7 @@ impl Introspector {
#[comemo::track]
impl Introspector {
/// Query for all matching elements.
pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
pub fn query(&self, selector: &Selector) -> EcoVec<Content> {
let hash = crate::util::hash128(selector);
if let Some(output) = self.queries.get(hash) {
return output;
@ -201,7 +195,7 @@ impl Introspector {
}
/// Query for the first element that matches the selector.
pub fn query_first(&self, selector: &Selector) -> Option<Prehashed<Content>> {
pub fn query_first(&self, selector: &Selector) -> Option<Content> {
match selector {
Selector::Location(location) => self.get(location).cloned(),
_ => self.query(selector).first().cloned(),
@ -209,7 +203,7 @@ impl Introspector {
}
/// Query for a unique element with the label.
pub fn query_label(&self, label: Label) -> StrResult<&Prehashed<Content>> {
pub fn query_label(&self, label: Label) -> StrResult<&Content> {
let indices = self.labels.get(&label).ok_or_else(|| {
eco_format!("label `{}` does not exist in the document", label.repr())
})?;
@ -268,14 +262,14 @@ impl Debug for Introspector {
/// Caches queries.
#[derive(Default)]
struct QueryCache(RwLock<HashMap<u128, EcoVec<Prehashed<Content>>>>);
struct QueryCache(RwLock<HashMap<u128, EcoVec<Content>>>);
impl QueryCache {
fn get(&self, hash: u128) -> Option<EcoVec<Prehashed<Content>>> {
fn get(&self, hash: u128) -> Option<EcoVec<Content>> {
self.0.read().unwrap().get(&hash).cloned()
}
fn insert(&self, hash: u128, output: EcoVec<Prehashed<Content>>) {
fn insert(&self, hash: u128, output: EcoVec<Content>) {
self.0.write().unwrap().insert(hash, output);
}

View file

@ -156,7 +156,5 @@ pub fn query(
) -> Array {
let _ = location;
let vec = engine.introspector.query(&target.0);
vec.into_iter()
.map(|elem| Value::Content(elem.into_inner()))
.collect()
vec.into_iter().map(Value::Content).collect()
}

View file

@ -1,7 +1,5 @@
use std::fmt::{self, Debug, Formatter};
use comemo::Prehashed;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
@ -24,7 +22,7 @@ use crate::util::Numeric;
pub struct FlowElem {
/// The children that will be arranges into a flow.
#[variadic]
pub children: Vec<Prehashed<Content>>,
pub children: Vec<Content>,
}
impl LayoutMultiple for Packed<FlowElem> {
@ -43,7 +41,7 @@ impl LayoutMultiple for Packed<FlowElem> {
}
let mut layouter = FlowLayouter::new(regions, styles);
for mut child in self.children().iter().map(|c| &**c) {
for mut child in self.children().iter() {
let outer = styles;
let mut styles = styles;
if let Some(styled) = child.to_packed::<StyledElem>() {

View file

@ -1,7 +1,7 @@
mod linebreak;
mod shaping;
use comemo::{Prehashed, Tracked, TrackedMut};
use comemo::{Tracked, TrackedMut};
use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript};
@ -30,7 +30,7 @@ use crate::World;
/// Layouts content inline.
pub(crate) fn layout_inline(
children: &[Prehashed<Content>],
children: &[Content],
engine: &mut Engine,
styles: StyleChain,
consecutive: bool,
@ -40,7 +40,7 @@ pub(crate) fn layout_inline(
#[comemo::memoize]
#[allow(clippy::too_many_arguments)]
fn cached(
children: &[Prehashed<Content>],
children: &[Content],
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
@ -404,7 +404,7 @@ impl<'a> Line<'a> {
/// also performs string-level preprocessing like case transformations.
#[allow(clippy::type_complexity)]
fn collect<'a>(
children: &'a [Prehashed<Content>],
children: &'a [Content],
engine: &mut Engine<'_>,
styles: &'a StyleChain<'a>,
region: Size,
@ -414,7 +414,7 @@ fn collect<'a>(
let mut quoter = SmartQuoter::new();
let mut segments = Vec::with_capacity(2 + children.len());
let mut spans = SpanMapper::new();
let mut iter = children.iter().map(|c| &**c).peekable();
let mut iter = children.iter().peekable();
let first_line_indent = ParElem::first_line_indent_in(*styles);
if !first_line_indent.is_zero()
@ -539,7 +539,7 @@ fn collect<'a>(
/// Prepare paragraph layout by shaping the whole paragraph.
fn prepare<'a>(
engine: &mut Engine,
children: &'a [Prehashed<Content>],
children: &'a [Content],
text: &'a str,
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
spans: SpanMapper,
@ -752,7 +752,7 @@ fn is_compatible(a: Script, b: Script) -> bool {
/// paragraph.
fn shared_get<T: PartialEq>(
styles: StyleChain<'_>,
children: &[Prehashed<Content>],
children: &[Content],
getter: fn(StyleChain) -> T,
) -> Option<T> {
let value = getter(styles);

View file

@ -1,6 +1,5 @@
use std::f64::consts::SQRT_2;
use comemo::Prehashed;
use ecow::EcoString;
use rustybuzz::Feature;
use ttf_parser::gsub::{AlternateSubstitution, SingleSubstitution, SubstitutionSubtable};
@ -288,7 +287,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
// to extend as far as needed.
let spaced = text.graphemes(true).nth(1).is_some();
let text = TextElem::packed(text).spanned(span);
let par = ParElem::new(vec![Prehashed::new(text)]);
let par = ParElem::new(vec![text]);
let frame = Packed::new(par)
.spanned(span)
.layout(self.engine, styles, false, Size::splat(Abs::inf()), false)?

View file

@ -1,9 +1,8 @@
use comemo::Prehashed;
use crate::foundations::{func, Cast, Content, Smart, Style, StyleChain};
use crate::layout::Abs;
use crate::math::{EquationElem, MathContext};
use crate::text::TextElem;
use crate::util::LazyHash;
/// Bold font style in math.
///
@ -253,17 +252,17 @@ pub fn scaled_font_size(ctx: &MathContext, styles: StyleChain) -> Abs {
}
/// Styles something as cramped.
pub fn style_cramped() -> Prehashed<Style> {
pub fn style_cramped() -> LazyHash<Style> {
EquationElem::set_cramped(true).wrap()
}
/// The style for subscripts in the current style.
pub fn style_for_subscript(styles: StyleChain) -> [Prehashed<Style>; 2] {
pub fn style_for_subscript(styles: StyleChain) -> [LazyHash<Style>; 2] {
[style_for_superscript(styles), EquationElem::set_cramped(true).wrap()]
}
/// The style for superscripts in the current style.
pub fn style_for_superscript(styles: StyleChain) -> Prehashed<Style> {
pub fn style_for_superscript(styles: StyleChain) -> LazyHash<Style> {
EquationElem::set_size(match EquationElem::size_in(styles) {
MathSize::Display | MathSize::Text => MathSize::Script,
MathSize::Script | MathSize::ScriptScript => MathSize::ScriptScript,
@ -272,7 +271,7 @@ pub fn style_for_superscript(styles: StyleChain) -> Prehashed<Style> {
}
/// The style for numerators in the current style.
pub fn style_for_numerator(styles: StyleChain) -> Prehashed<Style> {
pub fn style_for_numerator(styles: StyleChain) -> LazyHash<Style> {
EquationElem::set_size(match EquationElem::size_in(styles) {
MathSize::Display => MathSize::Text,
MathSize::Text => MathSize::Script,
@ -282,7 +281,7 @@ pub fn style_for_numerator(styles: StyleChain) -> Prehashed<Style> {
}
/// The style for denominators in the current style.
pub fn style_for_denominator(styles: StyleChain) -> [Prehashed<Style>; 2] {
pub fn style_for_denominator(styles: StyleChain) -> [LazyHash<Style>; 2] {
[style_for_numerator(styles), EquationElem::set_cramped(true).wrap()]
}

View file

@ -6,7 +6,7 @@ use std::num::NonZeroUsize;
use std::path::Path;
use std::sync::Arc;
use comemo::{Prehashed, Tracked};
use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec};
use hayagriva::archive::ArchivedStyle;
use hayagriva::io::BibLaTeXError;
@ -40,7 +40,7 @@ use crate::syntax::{Span, Spanned};
use crate::text::{
FontStyle, Lang, LocalName, Region, SubElem, SuperElem, TextElem, WeightDelta,
};
use crate::util::{option_eq, NonZeroExt, PicoStr};
use crate::util::{option_eq, LazyHash, NonZeroExt, PicoStr};
use crate::World;
/// A bibliography / reference listing.
@ -438,7 +438,7 @@ fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> E
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct CslStyle {
name: Option<EcoString>,
style: Arc<Prehashed<citationberg::IndependentStyle>>,
style: Arc<LazyHash<citationberg::IndependentStyle>>,
}
impl CslStyle {
@ -495,7 +495,7 @@ impl CslStyle {
match hayagriva::archive::ArchivedStyle::by_name(name).map(ArchivedStyle::get) {
Some(citationberg::Style::Independent(style)) => Ok(Self {
name: Some(name.into()),
style: Arc::new(Prehashed::new(style)),
style: Arc::new(LazyHash::new(style)),
}),
_ => bail!("unknown style: `{name}`"),
}
@ -506,7 +506,7 @@ impl CslStyle {
pub fn from_data(data: &Bytes) -> StrResult<CslStyle> {
let text = std::str::from_utf8(data.as_slice()).map_err(FileError::from)?;
citationberg::IndependentStyle::from_xml(text)
.map(|style| Self { name: None, style: Arc::new(Prehashed::new(style)) })
.map(|style| Self { name: None, style: Arc::new(LazyHash::new(style)) })
.map_err(|err| eco_format!("failed to load CSL style ({err})"))
}
@ -606,7 +606,7 @@ struct Generator<'a> {
/// The document's bibliography.
bibliography: Packed<BibliographyElem>,
/// The document's citation groups.
groups: EcoVec<Prehashed<Content>>,
groups: EcoVec<Content>,
/// Details about each group that are accumulated while driving hayagriva's
/// bibliography driver and needed when processing hayagriva's output.
infos: Vec<GroupInfo>,

View file

@ -213,7 +213,7 @@ impl Show for Packed<OutlineElem> {
let Some(entry) = OutlineEntry::from_outlinable(
engine,
self.span(),
elem.clone().into_inner(),
elem.clone(),
self.fill(styles),
)?
else {

View file

@ -1,7 +1,5 @@
use std::fmt::{self, Debug, Formatter};
use comemo::Prehashed;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
@ -106,7 +104,7 @@ pub struct ParElem {
/// The paragraph's children.
#[internal]
#[variadic]
pub children: Vec<Prehashed<Content>>,
pub children: Vec<Content>,
}
impl Construct for ParElem {

View file

@ -151,7 +151,7 @@ impl Synthesize for Packed<RefElem> {
let target = *elem.target();
if !BibliographyElem::has(engine, target) {
if let Ok(found) = engine.introspector.query_label(target).cloned() {
elem.push_element(Some(found.into_inner()));
elem.push_element(Some(found));
return Ok(());
}
}

View file

@ -0,0 +1,161 @@
use std::any::Any;
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use portable_atomic::{AtomicU128, Ordering};
use siphasher::sip128::{Hasher128, SipHasher13};
/// A wrapper type with lazily-computed hash.
///
/// This is useful if you want to pass large values of `T` to memoized
/// functions. Especially recursive structures like trees benefit from
/// intermediate prehashed nodes.
///
/// Note that for a value `v` of type `T`, `hash(v)` is not necessarily equal to
/// `hash(LazyHash::new(v))`. Writing the precomputed hash into a hasher's
/// state produces different output than writing the value's parts directly.
/// However, that seldomly matters as you are typically either dealing with
/// values of type `T` or with values of type `LazyHash<T>`, not a mix of both.
///
/// # Equality
/// Because Typst uses high-quality 128 bit hashes in all places, the risk of a
/// hash collision is reduced to an absolute minimum. Therefore, this type
/// additionally provides `PartialEq` and `Eq` implementations that compare by
/// hash instead of by value. For this to be correct, your hash implementation
/// **must feed all information relevant to the `PartialEq` impl to the
/// hasher.**
///
/// # Usage
/// If the value is expected to be cloned, it is best used inside of an `Arc`
/// or `Rc` to best re-use the hash once it has been computed.
pub struct LazyHash<T: ?Sized> {
/// The hash for the value.
hash: AtomicU128,
/// The underlying value.
value: T,
}
impl<T: Default> Default for LazyHash<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T> LazyHash<T> {
/// Wrap an item without pre-computed hash.
#[inline]
pub fn new(value: T) -> Self {
Self { hash: AtomicU128::new(0), value }
}
/// Wrap an item with a pre-computed hash.
///
/// **Important:** The hash must be correct for the value. This cannot be
/// enforced at compile time, so use with caution.
#[inline]
pub fn with_hash(value: T, hash: u128) -> Self {
Self { hash: AtomicU128::new(hash), value }
}
/// Return the wrapped value.
#[inline]
pub fn into_inner(self) -> T {
self.value
}
}
impl<T: Hash + ?Sized + 'static> LazyHash<T> {
/// Get the hash, returns zero if not computed yet.
#[inline]
pub fn hash(&self) -> u128 {
self.hash.load(Ordering::SeqCst)
}
/// Reset the hash to zero.
#[inline]
fn reset_hash(&mut self) {
// Because we have a mutable reference, we can skip the atomic
*self.hash.get_mut() = 0;
}
/// Get the hash or compute it if not set yet.
#[inline]
fn get_or_set_hash(&self) -> u128 {
let hash = self.hash();
if hash == 0 {
let hashed = hash_item(&self.value);
self.hash.store(hashed, Ordering::SeqCst);
hashed
} else {
hash
}
}
}
/// Hash the item.
#[inline]
fn hash_item<T: Hash + ?Sized + 'static>(item: &T) -> u128 {
// Also hash the TypeId because the type might be converted
// through an unsized coercion.
let mut state = SipHasher13::new();
item.type_id().hash(&mut state);
item.hash(&mut state);
state.finish128().as_u128()
}
impl<T: Hash + ?Sized + 'static> Hash for LazyHash<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u128(self.get_or_set_hash());
}
}
impl<T> From<T> for LazyHash<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Hash + ?Sized + 'static> Eq for LazyHash<T> {}
impl<T: Hash + ?Sized + 'static> PartialEq for LazyHash<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.get_or_set_hash() == other.get_or_set_hash()
}
}
impl<T: ?Sized> Deref for LazyHash<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: Hash + ?Sized + 'static> DerefMut for LazyHash<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.reset_hash();
&mut self.value
}
}
impl<T: Hash + Clone + 'static> Clone for LazyHash<T> {
fn clone(&self) -> Self {
Self {
hash: AtomicU128::new(self.hash()),
value: self.value.clone(),
}
}
}
impl<T: Debug> Debug for LazyHash<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.value.fmt(f)
}
}

View file

@ -6,11 +6,13 @@ pub mod fat;
mod macros;
mod bitset;
mod deferred;
mod hash;
mod pico;
mod scalar;
pub use self::bitset::BitSet;
pub use self::deferred::Deferred;
pub use self::hash::LazyHash;
pub use self::pico::PicoStr;
pub use self::scalar::Scalar;

View file

@ -10,7 +10,7 @@ use std::ffi::OsStr;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use comemo::{Prehashed, Tracked};
use comemo::Tracked;
use ecow::EcoString;
use crate::diag::{bail, At, SourceResult, StrResult};
@ -27,7 +27,7 @@ use crate::loading::Readable;
use crate::model::Figurable;
use crate::syntax::{Span, Spanned};
use crate::text::{families, Lang, LocalName, Region};
use crate::util::{option_eq, Numeric};
use crate::util::{option_eq, LazyHash, Numeric};
use crate::visualize::Path;
use crate::World;
@ -299,7 +299,7 @@ pub enum ImageFit {
///
/// Values of this type are cheap to clone and hash.
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Image(Arc<Prehashed<Repr>>);
pub struct Image(Arc<LazyHash<Repr>>);
/// The internal representation.
#[derive(Hash)]
@ -337,7 +337,7 @@ impl Image {
}
};
Ok(Self(Arc::new(Prehashed::new(Repr { kind, alt }))))
Ok(Self(Arc::new(LazyHash::new(Repr { kind, alt }))))
}
/// Create a possibly font-dependant image from a buffer and a format.
@ -359,7 +359,7 @@ impl Image {
}
};
Ok(Self(Arc::new(Prehashed::new(Repr { kind, alt }))))
Ok(Self(Arc::new(LazyHash::new(Repr { kind, alt }))))
}
/// The raw image data.