From 95cd6adf24cb14ede8896fbb0c610432960f4f3a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 13 May 2024 17:25:43 +0200 Subject: Factor out `typst-utils` crate (#4125) --- Cargo.lock | 11 ++ Cargo.toml | 1 + crates/typst-cli/src/compile.rs | 2 +- crates/typst-cli/src/world.rs | 2 +- crates/typst-ide/src/analyze.rs | 2 +- crates/typst-ide/src/complete.rs | 2 +- crates/typst-ide/src/tooltip.rs | 2 +- crates/typst-macros/src/elem.rs | 2 +- crates/typst-pdf/src/font.rs | 4 +- crates/typst-pdf/src/gradient.rs | 2 +- crates/typst-pdf/src/image.rs | 2 +- crates/typst-pdf/src/lib.rs | 4 +- crates/typst-pdf/src/page.rs | 2 +- crates/typst-pdf/src/pattern.rs | 2 +- crates/typst-svg/src/lib.rs | 2 +- crates/typst-svg/src/paint.rs | 2 +- crates/typst-svg/src/text.rs | 2 +- crates/typst-utils/Cargo.toml | 22 +++ crates/typst-utils/src/bitset.rs | 104 +++++++++++ crates/typst-utils/src/deferred.rs | 53 ++++++ crates/typst-utils/src/fat.rs | 55 ++++++ crates/typst-utils/src/hash.rs | 163 ++++++++++++++++ crates/typst-utils/src/lib.rs | 245 +++++++++++++++++++++++++ crates/typst-utils/src/macros.rs | 49 +++++ crates/typst-utils/src/pico.rs | 80 ++++++++ crates/typst-utils/src/scalar.rs | 201 ++++++++++++++++++++ crates/typst/Cargo.toml | 1 + crates/typst/src/eval/call.rs | 2 +- crates/typst/src/eval/ops.rs | 2 +- crates/typst/src/eval/tracer.rs | 2 +- crates/typst/src/foundations/bytes.rs | 2 +- crates/typst/src/foundations/content.rs | 4 +- crates/typst/src/foundations/dict.rs | 2 +- crates/typst/src/foundations/element.rs | 2 +- crates/typst/src/foundations/func.rs | 2 +- crates/typst/src/foundations/label.rs | 2 +- crates/typst/src/foundations/scope.rs | 2 +- crates/typst/src/foundations/str.rs | 7 + crates/typst/src/foundations/styles.rs | 2 +- crates/typst/src/foundations/ty.rs | 2 +- crates/typst/src/foundations/value.rs | 2 +- crates/typst/src/introspection/counter.rs | 2 +- crates/typst/src/introspection/introspector.rs | 4 +- crates/typst/src/introspection/location.rs | 2 +- crates/typst/src/layout/abs.rs | 12 +- crates/typst/src/layout/angle.rs | 12 +- crates/typst/src/layout/axes.rs | 2 +- crates/typst/src/layout/columns.rs | 2 +- crates/typst/src/layout/container.rs | 2 +- crates/typst/src/layout/corners.rs | 2 +- crates/typst/src/layout/em.rs | 12 +- crates/typst/src/layout/flow.rs | 2 +- crates/typst/src/layout/fr.rs | 12 +- crates/typst/src/layout/frame.rs | 2 +- crates/typst/src/layout/grid/cells.rs | 2 +- crates/typst/src/layout/grid/layout.rs | 2 +- crates/typst/src/layout/grid/lines.rs | 2 +- crates/typst/src/layout/grid/mod.rs | 2 +- crates/typst/src/layout/grid/rowspans.rs | 2 +- crates/typst/src/layout/inline/mod.rs | 2 +- crates/typst/src/layout/inline/shaping.rs | 2 +- crates/typst/src/layout/length.rs | 12 +- crates/typst/src/layout/page.rs | 2 +- crates/typst/src/layout/point.rs | 12 +- crates/typst/src/layout/ratio.rs | 14 +- crates/typst/src/layout/rel.rs | 2 +- crates/typst/src/layout/repeat.rs | 2 +- crates/typst/src/layout/sides.rs | 2 +- crates/typst/src/layout/size.rs | 12 +- crates/typst/src/layout/spacing.rs | 2 +- crates/typst/src/layout/stack.rs | 2 +- crates/typst/src/lib.rs | 6 +- crates/typst/src/math/equation.rs | 2 +- crates/typst/src/math/matrix.rs | 2 +- crates/typst/src/math/style.rs | 2 +- crates/typst/src/model/bibliography.rs | 4 +- crates/typst/src/model/figure.rs | 2 +- crates/typst/src/model/footnote.rs | 2 +- crates/typst/src/model/heading.rs | 2 +- crates/typst/src/model/outline.rs | 2 +- crates/typst/src/model/table.rs | 2 +- crates/typst/src/model/terms.rs | 2 +- crates/typst/src/realize/process.rs | 2 +- crates/typst/src/text/lang.rs | 2 +- crates/typst/src/util/bitset.rs | 104 ----------- crates/typst/src/util/deferred.rs | 53 ------ crates/typst/src/util/fat.rs | 55 ------ crates/typst/src/util/hash.rs | 161 ---------------- crates/typst/src/util/macros.rs | 47 ----- crates/typst/src/util/mod.rs | 245 ------------------------- crates/typst/src/util/pico.rs | 95 ---------- crates/typst/src/util/scalar.rs | 201 -------------------- crates/typst/src/visualize/image/mod.rs | 2 +- crates/typst/src/visualize/line.rs | 2 +- crates/typst/src/visualize/pattern.rs | 2 +- crates/typst/src/visualize/polygon.rs | 2 +- crates/typst/src/visualize/shape.rs | 2 +- crates/typst/src/visualize/stroke.rs | 2 +- docs/src/html.rs | 2 +- tests/src/tests.rs | 2 +- 100 files changed, 1119 insertions(+), 1088 deletions(-) create mode 100644 crates/typst-utils/Cargo.toml create mode 100644 crates/typst-utils/src/bitset.rs create mode 100644 crates/typst-utils/src/deferred.rs create mode 100644 crates/typst-utils/src/fat.rs create mode 100644 crates/typst-utils/src/hash.rs create mode 100644 crates/typst-utils/src/lib.rs create mode 100644 crates/typst-utils/src/macros.rs create mode 100644 crates/typst-utils/src/pico.rs create mode 100644 crates/typst-utils/src/scalar.rs delete mode 100644 crates/typst/src/util/bitset.rs delete mode 100644 crates/typst/src/util/deferred.rs delete mode 100644 crates/typst/src/util/fat.rs delete mode 100644 crates/typst/src/util/hash.rs delete mode 100644 crates/typst/src/util/macros.rs delete mode 100644 crates/typst/src/util/mod.rs delete mode 100644 crates/typst/src/util/pico.rs delete mode 100644 crates/typst/src/util/scalar.rs diff --git a/Cargo.lock b/Cargo.lock index 934fcbc7..8ea562e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2568,6 +2568,7 @@ dependencies = [ "typst-macros", "typst-syntax", "typst-timing", + "typst-utils", "unicode-bidi", "unicode-math-class", "unicode-script", @@ -2803,6 +2804,16 @@ dependencies = [ "typst-syntax", ] +[[package]] +name = "typst-utils" +version = "0.11.0" +dependencies = [ + "once_cell", + "portable-atomic", + "rayon", + "siphasher 1.0.0", +] + [[package]] name = "unic-langid" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index a8942094..d72eacd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ typst-render = { path = "crates/typst-render", version = "0.11.0" } typst-svg = { path = "crates/typst-svg", version = "0.11.0" } typst-syntax = { path = "crates/typst-syntax", version = "0.11.0" } typst-timing = { path = "crates/typst-timing", version = "0.11.0" } +typst-utils = { path = "crates/typst-utils", version = "0.11.0" } typst-assets = "0.11.0" typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "ee8ae61cca138dc92f9d818fc7f2fc046d0148c5" } az = "1.2" diff --git a/crates/typst-cli/src/compile.rs b/crates/typst-cli/src/compile.rs index bf9afc35..e145a820 100644 --- a/crates/typst-cli/src/compile.rs +++ b/crates/typst-cli/src/compile.rs @@ -380,7 +380,7 @@ impl ExportCache { /// Returns true if the entry is cached and appends the new hash to the /// cache (for the next compilation). pub fn is_cached(&self, i: usize, frame: &Frame) -> bool { - let hash = typst::util::hash128(frame); + let hash = typst::utils::hash128(frame); let mut cache = self.cache.upgradable_read(); if i >= cache.len() { diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs index 5b4259f4..57aecd42 100644 --- a/crates/typst-cli/src/world.rs +++ b/crates/typst-cli/src/world.rs @@ -325,7 +325,7 @@ impl SlotCell { // Read and hash the file. let result = timed!("loading file", load()); - let fingerprint = timed!("hashing file", typst::util::hash128(&result)); + let fingerprint = timed!("hashing file", typst::utils::hash128(&result)); // If the file contents didn't change, yield the old processed data. if mem::replace(&mut self.fingerprint, fingerprint) == fingerprint { diff --git a/crates/typst-ide/src/analyze.rs b/crates/typst-ide/src/analyze.rs index 47214481..748970b8 100644 --- a/crates/typst-ide/src/analyze.rs +++ b/crates/typst-ide/src/analyze.rs @@ -110,7 +110,7 @@ pub fn analyze_labels(document: &Document) -> (Vec<(Label, Option)>, // Bibliography keys. for (key, detail) in BibliographyElem::keys(document.introspector.track()) { - output.push((Label::new(&key), detail)); + output.push((Label::new(key.as_str()), detail)); } (output, split) diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs index 279e0493..2c4680d1 100644 --- a/crates/typst-ide/src/complete.rs +++ b/crates/typst-ide/src/complete.rs @@ -1229,7 +1229,7 @@ impl<'a> CompletionContext<'a> { /// Add completions for a castable. fn cast_completions(&mut self, cast: &'a CastInfo) { // Prevent duplicate completions from appearing. - if !self.seen_casts.insert(typst::util::hash128(cast)) { + if !self.seen_casts.insert(typst::utils::hash128(cast)) { return; } diff --git a/crates/typst-ide/src/tooltip.rs b/crates/typst-ide/src/tooltip.rs index 3416e5f8..3ecc4d8f 100644 --- a/crates/typst-ide/src/tooltip.rs +++ b/crates/typst-ide/src/tooltip.rs @@ -7,7 +7,7 @@ use typst::foundations::{repr, Capturer, CastInfo, Repr, Value}; use typst::layout::Length; use typst::model::Document; use typst::syntax::{ast, LinkedNode, Side, Source, SyntaxKind}; -use typst::util::{round_2, Numeric}; +use typst::utils::{round_2, Numeric}; use typst::World; use crate::analyze::{analyze_expr, analyze_labels}; diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs index 7734dd54..455a883e 100644 --- a/crates/typst-macros/src/elem.rs +++ b/crates/typst-macros/src/elem.rs @@ -799,7 +799,7 @@ fn create_capable_impl(element: &Elem) -> TokenStream { // Safety: The vtable function doesn't require initialized // data, so it's fine to use a dangling pointer. return Some(unsafe { - ::typst::util::fat::vtable(dangling as *const dyn #capability) + ::typst::utils::fat::vtable(dangling as *const dyn #capability) }); } } diff --git a/crates/typst-pdf/src/font.rs b/crates/typst-pdf/src/font.rs index 15d1a50e..5d32e5d9 100644 --- a/crates/typst-pdf/src/font.rs +++ b/crates/typst-pdf/src/font.rs @@ -9,7 +9,7 @@ use pdf_writer::{Filter, Finish, Name, Rect, Str}; use ttf_parser::{name_id, GlyphId, Tag}; use typst::layout::{Abs, Em, Ratio, Transform}; use typst::text::Font; -use typst::util::SliceExt; +use typst::utils::SliceExt; use unicode_properties::{GeneralCategory, UnicodeGeneralCategory}; use crate::page::{write_frame, PageContext}; @@ -320,7 +320,7 @@ fn subset_font(font: &Font, glyphs: &[u16]) -> Arc> { fn subset_tag(glyphs: &T) -> EcoString { const LEN: usize = 6; const BASE: u128 = 26; - let mut hash = typst::util::hash128(&glyphs); + let mut hash = typst::utils::hash128(&glyphs); let mut letter = [b'A'; LEN]; for l in letter.iter_mut() { *l = b'A' + (hash % BASE) as u8; diff --git a/crates/typst-pdf/src/gradient.rs b/crates/typst-pdf/src/gradient.rs index 7dd289a9..576c254e 100644 --- a/crates/typst-pdf/src/gradient.rs +++ b/crates/typst-pdf/src/gradient.rs @@ -6,7 +6,7 @@ use pdf_writer::types::{ColorSpaceOperand, FunctionShadingType}; use pdf_writer::writers::StreamShadingType; use pdf_writer::{Filter, Finish, Name, Ref}; use typst::layout::{Abs, Angle, Point, Quadrant, Ratio, Transform}; -use typst::util::Numeric; +use typst::utils::Numeric; use typst::visualize::{ Color, ColorSpace, Gradient, RatioOrAngle, RelativeTo, WeightedColor, }; diff --git a/crates/typst-pdf/src/image.rs b/crates/typst-pdf/src/image.rs index 312c2abb..7d108d6d 100644 --- a/crates/typst-pdf/src/image.rs +++ b/crates/typst-pdf/src/image.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use image::{DynamicImage, GenericImageView, Rgba}; use pdf_writer::{Chunk, Filter, Finish, Ref}; -use typst::util::Deferred; +use typst::utils::Deferred; use typst::visualize::{ ColorSpace, Image, ImageKind, RasterFormat, RasterImage, SvgImage, }; diff --git a/crates/typst-pdf/src/lib.rs b/crates/typst-pdf/src/lib.rs index e618f572..61906b1f 100644 --- a/crates/typst-pdf/src/lib.rs +++ b/crates/typst-pdf/src/lib.rs @@ -25,7 +25,7 @@ use typst::layout::{Abs, Dir, Em, Frame, PageRanges, Transform}; use typst::model::{Document, HeadingElem}; use typst::text::color::frame_for_glyph; use typst::text::{Font, Lang}; -use typst::util::Deferred; +use typst::utils::Deferred; use typst::visualize::Image; use xmp_writer::{DateTime, LangId, RenditionClass, Timezone, XmpWriter}; @@ -394,7 +394,7 @@ fn deflate_deferred(content: Vec) -> Deferred> { /// Create a base64-encoded hash of the value. fn hash_base64(value: &T) -> String { base64::engine::general_purpose::STANDARD - .encode(typst::util::hash128(value).to_be_bytes()) + .encode(typst::utils::hash128(value).to_be_bytes()) } /// Converts a datetime to a pdf-writer date. diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs index 1785e98e..557daa80 100644 --- a/crates/typst-pdf/src/page.rs +++ b/crates/typst-pdf/src/page.rs @@ -19,7 +19,7 @@ use typst::layout::{ use typst::model::{Destination, Numbering}; use typst::text::color::is_color_glyph; use typst::text::{Case, Font, TextItem, TextItemView}; -use typst::util::{Deferred, Numeric, SliceExt}; +use typst::utils::{Deferred, Numeric, SliceExt}; use typst::visualize::{ FixedStroke, Geometry, Image, LineCap, LineJoin, Paint, Path, PathItem, Shape, }; diff --git a/crates/typst-pdf/src/pattern.rs b/crates/typst-pdf/src/pattern.rs index 211c056c..7fb3d6e8 100644 --- a/crates/typst-pdf/src/pattern.rs +++ b/crates/typst-pdf/src/pattern.rs @@ -2,7 +2,7 @@ use ecow::eco_format; use pdf_writer::types::{ColorSpaceOperand, PaintType, TilingType}; use pdf_writer::{Filter, Finish, Name, Rect}; use typst::layout::{Abs, Ratio, Transform}; -use typst::util::Numeric; +use typst::utils::Numeric; use typst::visualize::{Pattern, RelativeTo}; use crate::color::PaintEncode; diff --git a/crates/typst-svg/src/lib.rs b/crates/typst-svg/src/lib.rs index 3b9c4401..798a354c 100644 --- a/crates/typst-svg/src/lib.rs +++ b/crates/typst-svg/src/lib.rs @@ -14,7 +14,7 @@ use typst::layout::{ Abs, Frame, FrameItem, FrameKind, GroupItem, Point, Ratio, Size, Transform, }; use typst::model::Document; -use typst::util::hash128; +use typst::utils::hash128; use typst::visualize::{Gradient, Pattern}; use xmlwriter::XmlWriter; diff --git a/crates/typst-svg/src/paint.rs b/crates/typst-svg/src/paint.rs index fa3812bc..a382bd9d 100644 --- a/crates/typst-svg/src/paint.rs +++ b/crates/typst-svg/src/paint.rs @@ -4,7 +4,7 @@ use ecow::{eco_format, EcoString}; use ttf_parser::OutlineBuilder; use typst::foundations::Repr; use typst::layout::{Angle, Axes, Frame, Quadrant, Ratio, Size, Transform}; -use typst::util::hash128; +use typst::utils::hash128; use typst::visualize::{Color, Gradient, Paint, Pattern, RatioOrAngle}; use xmlwriter::XmlWriter; diff --git a/crates/typst-svg/src/text.rs b/crates/typst-svg/src/text.rs index c29c7a68..dc5442f9 100644 --- a/crates/typst-svg/src/text.rs +++ b/crates/typst-svg/src/text.rs @@ -5,7 +5,7 @@ use ecow::EcoString; use ttf_parser::GlyphId; use typst::layout::{Abs, Point, Ratio, Size, Transform}; use typst::text::{Font, TextItem}; -use typst::util::hash128; +use typst::utils::hash128; use typst::visualize::{Image, Paint, RasterFormat, RelativeTo}; use crate::{SVGRenderer, State, SvgMatrix, SvgPathBuilder}; diff --git a/crates/typst-utils/Cargo.toml b/crates/typst-utils/Cargo.toml new file mode 100644 index 00000000..ba75e399 --- /dev/null +++ b/crates/typst-utils/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "typst-utils" +description = "Utilities for Typst." +version = { workspace = true } +rust-version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +categories = { workspace = true } +keywords = { workspace = true } +readme = { workspace = true } + +[dependencies] +once_cell = { workspace = true } +siphasher = { workspace = true } +portable-atomic = { workspace = true } +rayon = { workspace = true } + +[lints] +workspace = true diff --git a/crates/typst-utils/src/bitset.rs b/crates/typst-utils/src/bitset.rs new file mode 100644 index 00000000..cbac7a1e --- /dev/null +++ b/crates/typst-utils/src/bitset.rs @@ -0,0 +1,104 @@ +use std::fmt::{self, Debug, Formatter}; + +/// Efficiently stores a set of numbers which are expected to be very small +/// (< 32/64 depending on the architecture). +/// +/// Inserting a very small value is very cheap while inserting a large one may +/// be very expensive. +#[derive(Clone, PartialEq, Hash)] +pub struct BitSet { + /// Used to store values < BITS. + low: usize, + /// Used to store values > BITS. We have the extra `Box` to keep the memory + /// size of the `BitSet` down. + #[allow(clippy::box_collection)] + hi: Option>>, +} + +/// The number of bits per chunk. +const BITS: usize = usize::BITS as usize; + +impl BitSet { + /// Creates a new empty bit set. + pub fn new() -> Self { + Self { low: 0, hi: None } + } + + /// Inserts a number into the set. + pub fn insert(&mut self, value: usize) { + if value < BITS { + self.low |= 1 << value; + } else { + let chunk = value / BITS - 1; + let within = value % BITS; + let vec = self.hi.get_or_insert_with(Default::default); + if chunk >= vec.len() { + vec.resize(chunk + 1, 0); + } + vec[chunk] |= 1 << within; + } + } + + /// Whether a number is present in the set. + pub fn contains(&self, value: usize) -> bool { + if value < BITS { + (self.low & (1 << value)) != 0 + } else { + let Some(hi) = &self.hi else { return false }; + let chunk = value / BITS - 1; + let within = value % BITS; + let Some(bits) = hi.get(chunk) else { return false }; + (bits & (1 << within)) != 0 + } + } +} + +impl Default for BitSet { + fn default() -> Self { + Self::new() + } +} + +impl Debug for BitSet { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut list = f.debug_list(); + let chunks = 1 + self.hi.as_ref().map_or(0, |v| v.len()); + for v in 0..chunks * BITS { + if self.contains(v) { + list.entry(&v); + } + } + list.finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bitset() { + let mut set = BitSet::new(); + assert!(!set.contains(0)); + assert!(!set.contains(5)); + set.insert(0); + set.insert(1); + set.insert(5); + set.insert(64); + set.insert(105); + set.insert(208); + assert!(set.contains(0)); + assert!(set.contains(1)); + assert!(!set.contains(2)); + assert!(set.contains(5)); + assert!(!set.contains(63)); + assert!(set.contains(64)); + assert!(!set.contains(65)); + assert!(!set.contains(104)); + assert!(set.contains(105)); + assert!(!set.contains(106)); + assert!(set.contains(208)); + assert!(!set.contains(209)); + assert_eq!(format!("{set:?}"), "[0, 1, 5, 64, 105, 208]"); + } +} diff --git a/crates/typst-utils/src/deferred.rs b/crates/typst-utils/src/deferred.rs new file mode 100644 index 00000000..46cd6e88 --- /dev/null +++ b/crates/typst-utils/src/deferred.rs @@ -0,0 +1,53 @@ +use std::sync::Arc; + +use once_cell::sync::OnceCell; + +/// A value that is lazily executed on another thread. +/// +/// Execution will be started in the background and can be waited on. +pub struct Deferred(Arc>); + +impl Deferred { + /// Creates a new deferred value. + /// + /// The closure will be called on a secondary thread such that the value + /// can be initialized in parallel. + pub fn new(f: F) -> Self + where + F: FnOnce() -> T + Send + Sync + 'static, + { + let inner = Arc::new(OnceCell::new()); + let cloned = Arc::clone(&inner); + rayon::spawn(move || { + // Initialize the value if it hasn't been initialized yet. + // We do this to avoid panicking in case it was set externally. + cloned.get_or_init(f); + }); + Self(inner) + } + + /// Waits on the value to be initialized. + /// + /// If the value has already been initialized, this will return + /// immediately. Otherwise, this will block until the value is + /// initialized in another thread. + pub fn wait(&self) -> &T { + // Fast path if the value is already available. We don't want to yield + // to rayon in that case. + if let Some(value) = self.0.get() { + return value; + } + + // Ensure that we yield to give the deferred value a chance to compute + // single-threaded platforms (for WASM compatibility). + while let Some(rayon::Yield::Executed) = rayon::yield_now() {} + + self.0.wait() + } +} + +impl Clone for Deferred { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} diff --git a/crates/typst-utils/src/fat.rs b/crates/typst-utils/src/fat.rs new file mode 100644 index 00000000..d3c9bb20 --- /dev/null +++ b/crates/typst-utils/src/fat.rs @@ -0,0 +1,55 @@ +//! Fat pointer handling. +//! +//! This assumes the memory representation of fat pointers. Although it is not +//! guaranteed by Rust, it's improbable that it will change. Still, when the +//! pointer metadata APIs are stable, we should definitely move to them: +//! + +use std::alloc::Layout; +use std::mem; + +/// Create a fat pointer from a data address and a vtable address. +/// +/// # Safety +/// Must only be called when `T` is a `dyn Trait`. The data address must point +/// to a value whose type implements the trait of `T` and the `vtable` must have +/// been extracted with [`vtable`]. +#[track_caller] +pub unsafe fn from_raw_parts(data: *const (), vtable: *const ()) -> *const T { + let fat = FatPointer { data, vtable }; + debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); + mem::transmute_copy::(&fat) +} + +/// Create a mutable fat pointer from a data address and a vtable address. +/// +/// # Safety +/// Must only be called when `T` is a `dyn Trait`. The data address must point +/// to a value whose type implements the trait of `T` and the `vtable` must have +/// been extracted with [`vtable`]. +#[track_caller] +pub unsafe fn from_raw_parts_mut(data: *mut (), vtable: *const ()) -> *mut T { + let fat = FatPointer { data, vtable }; + debug_assert_eq!(Layout::new::<*mut T>(), Layout::new::()); + mem::transmute_copy::(&fat) +} + +/// Extract the address to a trait object's vtable. +/// +/// # Safety +/// Must only be called when `T` is a `dyn Trait`. +#[track_caller] +pub unsafe fn vtable(ptr: *const T) -> *const () { + debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); + mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable +} + +/// The memory representation of a trait object pointer. +/// +/// Although this is not guaranteed by Rust, it's improbable that it will +/// change. +#[repr(C)] +struct FatPointer { + data: *const (), + vtable: *const (), +} diff --git a/crates/typst-utils/src/hash.rs b/crates/typst-utils/src/hash.rs new file mode 100644 index 00000000..82bb76af --- /dev/null +++ b/crates/typst-utils/src/hash.rs @@ -0,0 +1,163 @@ +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`, 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 { + /// The hash for the value. + hash: AtomicU128, + /// The underlying value. + value: T, +} + +impl Default for LazyHash { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl LazyHash { + /// Wraps 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 reuse(value: T, existing: &LazyHash) -> Self { + LazyHash { hash: AtomicU128::new(existing.load_hash()), value } + } + + /// Returns the wrapped value. + #[inline] + pub fn into_inner(self) -> T { + self.value + } +} + +impl LazyHash { + /// Get the hash, returns zero if not computed yet. + #[inline] + fn load_hash(&self) -> u128 { + self.hash.load(Ordering::SeqCst) + } +} + +impl LazyHash { + /// Get the hash or compute it if not set yet. + #[inline] + fn load_or_compute_hash(&self) -> u128 { + let hash = self.load_hash(); + if hash == 0 { + let hashed = hash_item(&self.value); + self.hash.store(hashed, Ordering::SeqCst); + hashed + } else { + hash + } + } + + /// 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; + } +} + +/// Hash the item. +#[inline] +fn hash_item(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 Hash for LazyHash { + #[inline] + fn hash(&self, state: &mut H) { + state.write_u128(self.load_or_compute_hash()); + } +} + +impl From for LazyHash { + #[inline] + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Eq for LazyHash {} + +impl PartialEq for LazyHash { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.load_or_compute_hash() == other.load_or_compute_hash() + } +} + +impl Deref for LazyHash { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl DerefMut for LazyHash { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.reset_hash(); + &mut self.value + } +} + +impl Clone for LazyHash { + fn clone(&self) -> Self { + Self { + hash: AtomicU128::new(self.load_hash()), + value: self.value.clone(), + } + } +} + +impl Debug for LazyHash { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.value.fmt(f) + } +} diff --git a/crates/typst-utils/src/lib.rs b/crates/typst-utils/src/lib.rs new file mode 100644 index 00000000..e0a2c835 --- /dev/null +++ b/crates/typst-utils/src/lib.rs @@ -0,0 +1,245 @@ +//! Utilities for Typst. + +pub mod fat; + +#[macro_use] +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; + +use std::fmt::{Debug, Formatter}; +use std::hash::Hash; +use std::iter::{Chain, Flatten, Rev}; +use std::num::NonZeroUsize; +use std::ops::{Add, Deref, Div, Mul, Neg, Sub}; +use std::sync::Arc; + +use siphasher::sip128::{Hasher128, SipHasher13}; + +/// Turn a closure into a struct implementing [`Debug`]. +pub fn debug(f: F) -> impl Debug +where + F: Fn(&mut Formatter) -> std::fmt::Result, +{ + struct Wrapper(F); + + impl Debug for Wrapper + where + F: Fn(&mut Formatter) -> std::fmt::Result, + { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0(f) + } + } + + Wrapper(f) +} + +/// Calculate a 128-bit siphash of a value. +pub fn hash128(value: &T) -> u128 { + let mut state = SipHasher13::new(); + value.hash(&mut state); + state.finish128().as_u128() +} + +/// An extra constant for [`NonZeroUsize`]. +pub trait NonZeroExt { + /// The number `1`. + const ONE: Self; +} + +impl NonZeroExt for NonZeroUsize { + const ONE: Self = match Self::new(1) { + Some(v) => v, + None => unreachable!(), + }; +} + +/// Extra methods for [`Arc`]. +pub trait ArcExt { + /// Takes the inner value if there is exactly one strong reference and + /// clones it otherwise. + fn take(self) -> T; +} + +impl ArcExt for Arc { + fn take(self) -> T { + match Arc::try_unwrap(self) { + Ok(v) => v, + Err(rc) => (*rc).clone(), + } + } +} + +/// Extra methods for [`[T]`](slice). +pub trait SliceExt { + /// Split a slice into consecutive runs with the same key and yield for + /// each such run the key and the slice of elements with that key. + fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> + where + F: FnMut(&T) -> K, + K: PartialEq; +} + +impl SliceExt for [T] { + fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> { + GroupByKey { slice: self, f } + } +} + +/// This struct is created by [`SliceExt::group_by_key`]. +pub struct GroupByKey<'a, T, F> { + slice: &'a [T], + f: F, +} + +impl<'a, T, K, F> Iterator for GroupByKey<'a, T, F> +where + F: FnMut(&T) -> K, + K: PartialEq, +{ + type Item = (K, &'a [T]); + + fn next(&mut self) -> Option { + let mut iter = self.slice.iter(); + let key = (self.f)(iter.next()?); + let count = 1 + iter.take_while(|t| (self.f)(t) == key).count(); + let (head, tail) = self.slice.split_at(count); + self.slice = tail; + Some((key, head)) + } +} + +/// Adapter for reversing iterators conditionally. +pub trait MaybeReverseIter { + type RevIfIter; + + /// Reverse this iterator (apply .rev()) based on some condition. + fn rev_if(self, condition: bool) -> Self::RevIfIter + where + Self: Sized; +} + +impl MaybeReverseIter for I { + type RevIfIter = + Chain>, Flatten>>>; + + fn rev_if(self, condition: bool) -> Self::RevIfIter + where + Self: Sized, + { + let (maybe_self_iter, maybe_rev_iter) = + if condition { (None, Some(self.rev())) } else { (Some(self), None) }; + + maybe_self_iter + .into_iter() + .flatten() + .chain(maybe_rev_iter.into_iter().flatten()) + } +} + +/// Check if the [`Option`]-wrapped L is same to R. +pub fn option_eq(left: Option, other: R) -> bool +where + L: PartialEq, +{ + left.is_some_and(|v| v == other) +} + +/// A container around a static reference that is cheap to clone and hash. +#[derive(Debug)] +pub struct Static(pub &'static T); + +impl Deref for Static { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl Copy for Static {} + +impl Clone for Static { + fn clone(&self) -> Self { + *self + } +} + +impl Eq for Static {} + +impl PartialEq for Static { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.0, other.0) + } +} + +impl Hash for Static { + fn hash(&self, state: &mut H) { + state.write_usize(self.0 as *const _ as _); + } +} + +/// Generic access to a structure's components. +pub trait Get { + /// The structure's component type. + type Component; + + /// Borrow the component for the specified index. + fn get_ref(&self, index: Index) -> &Self::Component; + + /// Borrow the component for the specified index mutably. + fn get_mut(&mut self, index: Index) -> &mut Self::Component; + + /// Convenience method for getting a copy of a component. + fn get(self, index: Index) -> Self::Component + where + Self: Sized, + Self::Component: Copy, + { + *self.get_ref(index) + } + + /// Convenience method for setting a component. + fn set(&mut self, index: Index, component: Self::Component) { + *self.get_mut(index) = component; + } +} + +/// A numeric type. +pub trait Numeric: + Sized + + Debug + + Copy + + PartialEq + + Neg + + Add + + Sub + + Mul + + Div +{ + /// The identity element for addition. + fn zero() -> Self; + + /// Whether `self` is zero. + fn is_zero(self) -> bool { + self == Self::zero() + } + + /// Whether `self` consists only of finite parts. + fn is_finite(self) -> bool; +} + +/// Round a float to two decimal places. +pub fn round_2(value: f64) -> f64 { + (value * 100.0).round() / 100.0 +} diff --git a/crates/typst-utils/src/macros.rs b/crates/typst-utils/src/macros.rs new file mode 100644 index 00000000..dfe0c319 --- /dev/null +++ b/crates/typst-utils/src/macros.rs @@ -0,0 +1,49 @@ +/// Implement the `Sub` trait based on existing `Neg` and `Add` impls. +#[macro_export] +macro_rules! sub_impl { + ($a:ident - $b:ident -> $c:ident) => { + impl std::ops::Sub<$b> for $a { + type Output = $c; + + fn sub(self, other: $b) -> $c { + self + -other + } + } + }; +} + +/// Implement an assign trait based on an existing non-assign trait. +#[macro_export] +macro_rules! assign_impl { + ($a:ident += $b:ident) => { + impl std::ops::AddAssign<$b> for $a { + fn add_assign(&mut self, other: $b) { + *self = *self + other; + } + } + }; + + ($a:ident -= $b:ident) => { + impl std::ops::SubAssign<$b> for $a { + fn sub_assign(&mut self, other: $b) { + *self = *self - other; + } + } + }; + + ($a:ident *= $b:ident) => { + impl std::ops::MulAssign<$b> for $a { + fn mul_assign(&mut self, other: $b) { + *self = *self * other; + } + } + }; + + ($a:ident /= $b:ident) => { + impl std::ops::DivAssign<$b> for $a { + fn div_assign(&mut self, other: $b) { + *self = *self / other; + } + } + }; +} diff --git a/crates/typst-utils/src/pico.rs b/crates/typst-utils/src/pico.rs new file mode 100644 index 00000000..b58d6809 --- /dev/null +++ b/crates/typst-utils/src/pico.rs @@ -0,0 +1,80 @@ +use std::cmp::Ordering; +use std::collections::HashMap; +use std::fmt::{self, Debug, Formatter}; +use std::sync::RwLock; + +use once_cell::sync::Lazy; + +/// The global string interner. +static INTERNER: Lazy> = + Lazy::new(|| RwLock::new(Interner { to_id: HashMap::new(), from_id: Vec::new() })); + +/// A string interner. +struct Interner { + to_id: HashMap<&'static str, PicoStr>, + from_id: Vec<&'static str>, +} + +/// An interned string. +/// +/// The API is purposefully kept small. This is because it might be relatively +/// slow to look up a string in the interner, so we want to avoid doing it +/// unnecessarily. For this reason, the user should use the [`PicoStr::resolve`] +/// method to get the underlying string, such that the lookup is done only once. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct PicoStr(u32); + +impl PicoStr { + /// Creates a new interned string. + pub fn new(string: &str) -> Self { + if let Some(&id) = INTERNER.read().unwrap().to_id.get(string) { + return id; + } + + let mut interner = INTERNER.write().unwrap(); + let num = interner.from_id.len().try_into().expect("out of string ids"); + + // Create a new entry forever by leaking the string. PicoStr is only + // used for strings that aren't created en masse, so it is okay. + let id = Self(num); + let string = Box::leak(string.to_string().into_boxed_str()); + interner.to_id.insert(string, id); + interner.from_id.push(string); + id + } + + /// Resolves the interned string. + pub fn resolve(&self) -> &'static str { + INTERNER.read().unwrap().from_id[self.0 as usize] + } +} + +impl Debug for PicoStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.resolve().fmt(f) + } +} + +impl Ord for PicoStr { + fn cmp(&self, other: &Self) -> Ordering { + self.resolve().cmp(other.resolve()) + } +} + +impl PartialOrd for PicoStr { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl AsRef for PicoStr { + fn as_ref(&self) -> &str { + self.resolve() + } +} + +impl From<&str> for PicoStr { + fn from(value: &str) -> Self { + Self::new(value) + } +} diff --git a/crates/typst-utils/src/scalar.rs b/crates/typst-utils/src/scalar.rs new file mode 100644 index 00000000..4036c231 --- /dev/null +++ b/crates/typst-utils/src/scalar.rs @@ -0,0 +1,201 @@ +use std::cmp::Ordering; +use std::fmt::{self, Debug, Formatter}; +use std::hash::{Hash, Hasher}; +use std::iter::Sum; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, +}; + +use crate::Numeric; + +/// A 64-bit float that implements `Eq`, `Ord` and `Hash`. +/// +/// Panics if it's `NaN` during any of those operations. +#[derive(Default, Copy, Clone)] +pub struct Scalar(f64); + +impl Scalar { + /// The scalar containing `0.0`. + pub const ZERO: Self = Self(0.0); + + /// The scalar containing `1.0`. + pub const ONE: Self = Self(1.0); + + /// The scalar containing `f64::INFINITY`. + pub const INFINITY: Self = Self(f64::INFINITY); + + /// Creates a [`Scalar`] with the given value. + /// + /// If the value is NaN, then it is set to `0.0` in the result. + pub const fn new(x: f64) -> Self { + Self(if is_nan(x) { 0.0 } else { x }) + } + + /// Gets the value of this [`Scalar`]. + pub const fn get(self) -> f64 { + self.0 + } +} + +// We have to detect NaNs this way since `f64::is_nan` isn’t const +// on stable yet: +// ([tracking issue](https://github.com/rust-lang/rust/issues/57241)) +#[allow(clippy::unusual_byte_groupings)] +const fn is_nan(x: f64) -> bool { + // Safety: all bit patterns are valid for u64, and f64 has no padding bits. + // We cannot use `f64::to_bits` because it is not const. + let x_bits = unsafe { std::mem::transmute::(x) }; + (x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0 +} + +impl Numeric for Scalar { + fn zero() -> Self { + Self(0.0) + } + + fn is_finite(self) -> bool { + self.0.is_finite() + } +} + +impl Debug for Scalar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Eq for Scalar {} + +impl PartialEq for Scalar { + fn eq(&self, other: &Self) -> bool { + assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN"); + self.0 == other.0 + } +} + +impl PartialEq for Scalar { + fn eq(&self, other: &f64) -> bool { + self == &Self(*other) + } +} + +impl Ord for Scalar { + fn cmp(&self, other: &Self) -> Ordering { + self.0.partial_cmp(&other.0).expect("float is NaN") + } +} + +impl PartialOrd for Scalar { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Hash for Scalar { + fn hash(&self, state: &mut H) { + debug_assert!(!self.0.is_nan(), "float is NaN"); + self.0.to_bits().hash(state); + } +} + +impl From for Scalar { + fn from(float: f64) -> Self { + Self::new(float) + } +} + +impl From for f64 { + fn from(scalar: Scalar) -> Self { + scalar.0 + } +} + +impl Neg for Scalar { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::new(-self.0) + } +} + +impl> Add for Scalar { + type Output = Self; + + fn add(self, rhs: T) -> Self::Output { + Self::new(self.0 + rhs.into().0) + } +} + +impl> AddAssign for Scalar { + fn add_assign(&mut self, rhs: T) { + *self = *self + rhs.into(); + } +} + +impl> Sub for Scalar { + type Output = Self; + + fn sub(self, rhs: T) -> Self::Output { + Self::new(self.0 - rhs.into().0) + } +} + +impl> SubAssign for Scalar { + fn sub_assign(&mut self, rhs: T) { + *self = *self - rhs.into(); + } +} + +impl> Mul for Scalar { + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + Self::new(self.0 * rhs.into().0) + } +} + +impl> MulAssign for Scalar { + fn mul_assign(&mut self, rhs: T) { + *self = *self * rhs.into(); + } +} + +impl> Div for Scalar { + type Output = Self; + + fn div(self, rhs: T) -> Self::Output { + Self::new(self.0 / rhs.into().0) + } +} + +impl> DivAssign for Scalar { + fn div_assign(&mut self, rhs: T) { + *self = *self / rhs.into(); + } +} + +impl> Rem for Scalar { + type Output = Self; + + fn rem(self, rhs: T) -> Self::Output { + Self::new(self.0 % rhs.into().0) + } +} + +impl> RemAssign for Scalar { + fn rem_assign(&mut self, rhs: T) { + *self = *self % rhs.into(); + } +} + +impl Sum for Scalar { + fn sum>(iter: I) -> Self { + Self::new(iter.map(|s| s.0).sum()) + } +} + +impl<'a> Sum<&'a Self> for Scalar { + fn sum>(iter: I) -> Self { + Self::new(iter.map(|s| s.0).sum()) + } +} diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml index 8e5e224a..3934ff42 100644 --- a/crates/typst/Cargo.toml +++ b/crates/typst/Cargo.toml @@ -17,6 +17,7 @@ typst-assets = { workspace = true } typst-macros = { workspace = true } typst-syntax = { workspace = true } typst-timing = { workspace = true } +typst-utils = { workspace = true } az = { workspace = true } bitflags = { workspace = true } chinese-number = { workspace = true } diff --git a/crates/typst/src/eval/call.rs b/crates/typst/src/eval/call.rs index ddc05416..61e5f318 100644 --- a/crates/typst/src/eval/call.rs +++ b/crates/typst/src/eval/call.rs @@ -14,7 +14,7 @@ use crate::symbols::Symbol; use crate::syntax::ast::{self, AstNode}; use crate::syntax::{Span, Spanned, SyntaxNode}; use crate::text::TextElem; -use crate::util::LazyHash; +use crate::utils::LazyHash; use crate::World; impl Eval for ast::FuncCall<'_> { diff --git a/crates/typst/src/eval/ops.rs b/crates/typst/src/eval/ops.rs index 4088dbe9..13e64120 100644 --- a/crates/typst/src/eval/ops.rs +++ b/crates/typst/src/eval/ops.rs @@ -10,7 +10,7 @@ use crate::foundations::{format_str, Datetime, IntoValue, Regex, Repr, Value}; use crate::layout::{Alignment, Length, Rel}; use crate::syntax::ast::{self, AstNode}; use crate::text::TextElem; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::Stroke; impl Eval for ast::Unary<'_> { diff --git a/crates/typst/src/eval/tracer.rs b/crates/typst/src/eval/tracer.rs index dff51935..30c640f3 100644 --- a/crates/typst/src/eval/tracer.rs +++ b/crates/typst/src/eval/tracer.rs @@ -5,7 +5,7 @@ use ecow::EcoVec; use crate::diag::SourceDiagnostic; use crate::foundations::{Styles, Value}; use crate::syntax::{FileId, Span}; -use crate::util::hash128; +use crate::utils::hash128; /// Traces warnings and which values existed for an expression at a span. #[derive(Default, Clone)] diff --git a/crates/typst/src/foundations/bytes.rs b/crates/typst/src/foundations/bytes.rs index 4b8800e8..1e795859 100644 --- a/crates/typst/src/foundations/bytes.rs +++ b/crates/typst/src/foundations/bytes.rs @@ -8,7 +8,7 @@ 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; +use crate::utils::LazyHash; /// A sequence of bytes. /// diff --git a/crates/typst/src/foundations/content.rs b/crates/typst/src/foundations/content.rs index 5edbb8a1..f11c74c0 100644 --- a/crates/typst/src/foundations/content.rs +++ b/crates/typst/src/foundations/content.rs @@ -24,7 +24,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, LazyHash}; +use crate::utils::{fat, BitSet, LazyHash}; /// A piece of document content. /// @@ -711,7 +711,7 @@ impl Bounds for T { label: inner.label, location: inner.location, lifecycle: inner.lifecycle.clone(), - elem: LazyHash::with_hash(self.clone(), inner.elem.hash()), + elem: LazyHash::reuse(self.clone(), &inner.elem), }), span, } diff --git a/crates/typst/src/foundations/dict.rs b/crates/typst/src/foundations/dict.rs index e77d33ff..991f3f7a 100644 --- a/crates/typst/src/foundations/dict.rs +++ b/crates/typst/src/foundations/dict.rs @@ -12,7 +12,7 @@ use crate::foundations::{ array, cast, func, repr, scope, ty, Array, Module, Repr, Str, Value, }; use crate::syntax::is_ident; -use crate::util::ArcExt; +use crate::utils::ArcExt; /// Create a new [`Dict`] from key-value pairs. #[macro_export] diff --git a/crates/typst/src/foundations/element.rs b/crates/typst/src/foundations/element.rs index b543e76f..47077eb8 100644 --- a/crates/typst/src/foundations/element.rs +++ b/crates/typst/src/foundations/element.rs @@ -14,7 +14,7 @@ use crate::foundations::{ Styles, Value, }; use crate::text::{Lang, Region}; -use crate::util::Static; +use crate::utils::Static; #[doc(inline)] pub use typst_macros::elem; diff --git a/crates/typst/src/foundations/func.rs b/crates/typst/src/foundations/func.rs index 42f2c6ad..75abc7ad 100644 --- a/crates/typst/src/foundations/func.rs +++ b/crates/typst/src/foundations/func.rs @@ -12,7 +12,7 @@ use crate::foundations::{ Selector, Type, Value, }; use crate::syntax::{ast, Span, SyntaxNode}; -use crate::util::{LazyHash, Static}; +use crate::utils::{LazyHash, Static}; #[doc(inline)] pub use typst_macros::func; diff --git a/crates/typst/src/foundations/label.rs b/crates/typst/src/foundations/label.rs index 78778359..8f15d317 100644 --- a/crates/typst/src/foundations/label.rs +++ b/crates/typst/src/foundations/label.rs @@ -1,7 +1,7 @@ use ecow::{eco_format, EcoString}; use crate::foundations::{func, scope, ty, Repr}; -use crate::util::PicoStr; +use crate::utils::PicoStr; /// A label for an element. /// diff --git a/crates/typst/src/foundations/scope.rs b/crates/typst/src/foundations/scope.rs index 870fa5b0..0cbbe923 100644 --- a/crates/typst/src/foundations/scope.rs +++ b/crates/typst/src/foundations/scope.rs @@ -9,7 +9,7 @@ use crate::foundations::{ Element, Func, IntoValue, Module, NativeElement, NativeFunc, NativeFuncData, NativeType, Type, Value, }; -use crate::util::Static; +use crate::utils::Static; use crate::Library; #[doc(inline)] diff --git a/crates/typst/src/foundations/str.rs b/crates/typst/src/foundations/str.rs index 515a4e21..61116d85 100644 --- a/crates/typst/src/foundations/str.rs +++ b/crates/typst/src/foundations/str.rs @@ -16,6 +16,7 @@ use crate::foundations::{ }; use crate::layout::Alignment; use crate::syntax::{Span, Spanned}; +use crate::utils::PicoStr; /// Create a new [`Str`] from a format string. #[macro_export] @@ -750,6 +751,12 @@ cast! { v: Str => v.into(), } +cast! { + PicoStr, + self => Value::Str(self.resolve().into()), + v: Str => v.as_str().into(), +} + cast! { String, self => Value::Str(self.into()), diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index 9b434a1e..7a75c5d5 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -16,7 +16,7 @@ use crate::foundations::{ use crate::introspection::Locatable; use crate::syntax::Span; use crate::text::{FontFamily, FontList, TextElem}; -use crate::util::LazyHash; +use crate::utils::LazyHash; /// Provides access to active styles. /// diff --git a/crates/typst/src/foundations/ty.rs b/crates/typst/src/foundations/ty.rs index 485bfb45..f199b85e 100644 --- a/crates/typst/src/foundations/ty.rs +++ b/crates/typst/src/foundations/ty.rs @@ -6,7 +6,7 @@ use once_cell::sync::Lazy; use crate::diag::StrResult; use crate::foundations::{cast, func, Func, NativeFuncData, Repr, Scope, Value}; -use crate::util::Static; +use crate::utils::Static; #[rustfmt::skip] #[doc(inline)] diff --git a/crates/typst/src/foundations/value.rs b/crates/typst/src/foundations/value.rs index f661228a..5d99a586 100644 --- a/crates/typst/src/foundations/value.rs +++ b/crates/typst/src/foundations/value.rs @@ -20,7 +20,7 @@ use crate::layout::{Abs, Angle, Em, Fr, Length, Ratio, Rel}; use crate::symbols::Symbol; use crate::syntax::{ast, Span}; use crate::text::{RawContent, RawElem, TextElem}; -use crate::util::ArcExt; +use crate::utils::ArcExt; use crate::visualize::{Color, Gradient, Pattern}; /// A computational value. diff --git a/crates/typst/src/introspection/counter.rs b/crates/typst/src/introspection/counter.rs index dd2fdcff..93f954a2 100644 --- a/crates/typst/src/introspection/counter.rs +++ b/crates/typst/src/introspection/counter.rs @@ -18,7 +18,7 @@ use crate::layout::{Frame, FrameItem, PageElem}; use crate::math::EquationElem; use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern}; use crate::syntax::Span; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::World; /// Counts through pages, elements, and more. diff --git a/crates/typst/src/introspection/introspector.rs b/crates/typst/src/introspection/introspector.rs index e8a4dccc..93dec21c 100644 --- a/crates/typst/src/introspection/introspector.rs +++ b/crates/typst/src/introspection/introspector.rs @@ -13,7 +13,7 @@ use crate::foundations::{Content, Label, Repr, Selector}; use crate::introspection::{Location, Meta}; use crate::layout::{Frame, FrameItem, Page, Point, Position, Transform}; use crate::model::Numbering; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; /// Can be queried for elements and their positions. #[derive(Clone)] @@ -108,7 +108,7 @@ impl Introspector { impl Introspector { /// Query for all matching elements. pub fn query(&self, selector: &Selector) -> EcoVec { - let hash = crate::util::hash128(selector); + let hash = crate::utils::hash128(selector); if let Some(output) = self.queries.get(hash) { return output; } diff --git a/crates/typst/src/introspection/location.rs b/crates/typst/src/introspection/location.rs index 4b02df6c..7b279b39 100644 --- a/crates/typst/src/introspection/location.rs +++ b/crates/typst/src/introspection/location.rs @@ -38,7 +38,7 @@ impl Location { /// locations for reference entries from the bibliography's location. pub fn variant(self, n: usize) -> Self { Self { - hash: crate::util::hash128(&(self.hash, n)), + hash: crate::utils::hash128(&(self.hash, n)), ..self } } diff --git a/crates/typst/src/layout/abs.rs b/crates/typst/src/layout/abs.rs index 5c07c5a0..451a1b5c 100644 --- a/crates/typst/src/layout/abs.rs +++ b/crates/typst/src/layout/abs.rs @@ -5,7 +5,7 @@ use std::ops::{Add, Div, Mul, Neg, Rem}; use ecow::EcoString; use crate::foundations::{cast, repr, Fold, Repr, Value}; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; /// An absolute length. #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -162,7 +162,7 @@ impl Add for Abs { } } -sub_impl!(Abs - Abs -> Abs); +typst_utils::sub_impl!(Abs - Abs -> Abs); impl Mul for Abs { type Output = Self; @@ -196,10 +196,10 @@ impl Div for Abs { } } -assign_impl!(Abs += Abs); -assign_impl!(Abs -= Abs); -assign_impl!(Abs *= f64); -assign_impl!(Abs /= f64); +typst_utils::assign_impl!(Abs += Abs); +typst_utils::assign_impl!(Abs -= Abs); +typst_utils::assign_impl!(Abs *= f64); +typst_utils::assign_impl!(Abs /= f64); impl Rem for Abs { type Output = Self; diff --git a/crates/typst/src/layout/angle.rs b/crates/typst/src/layout/angle.rs index 1cded9a3..63e30186 100644 --- a/crates/typst/src/layout/angle.rs +++ b/crates/typst/src/layout/angle.rs @@ -6,7 +6,7 @@ use std::ops::{Add, Div, Mul, Neg}; use ecow::EcoString; use crate::foundations::{func, repr, scope, ty, Repr}; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; /// An angle describing a rotation. /// @@ -155,7 +155,7 @@ impl Add for Angle { } } -sub_impl!(Angle - Angle -> Angle); +typst_utils::sub_impl!(Angle - Angle -> Angle); impl Mul for Angle { type Output = Self; @@ -189,10 +189,10 @@ impl Div for Angle { } } -assign_impl!(Angle += Angle); -assign_impl!(Angle -= Angle); -assign_impl!(Angle *= f64); -assign_impl!(Angle /= f64); +typst_utils::assign_impl!(Angle += Angle); +typst_utils::assign_impl!(Angle -= Angle); +typst_utils::assign_impl!(Angle *= f64); +typst_utils::assign_impl!(Angle /= f64); impl Sum for Angle { fn sum>(iter: I) -> Self { diff --git a/crates/typst/src/layout/axes.rs b/crates/typst/src/layout/axes.rs index 82cb4aee..6f2ab70d 100644 --- a/crates/typst/src/layout/axes.rs +++ b/crates/typst/src/layout/axes.rs @@ -5,7 +5,7 @@ use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not}; use crate::diag::bail; use crate::foundations::{array, cast, Array, Resolve, Smart, StyleChain}; use crate::layout::{Abs, Dir, Length, Ratio, Rel}; -use crate::util::Get; +use crate::utils::Get; /// A container with a horizontal and vertical component. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] diff --git a/crates/typst/src/layout/columns.rs b/crates/typst/src/layout/columns.rs index 04dc9275..f3812311 100644 --- a/crates/typst/src/layout/columns.rs +++ b/crates/typst/src/layout/columns.rs @@ -9,7 +9,7 @@ use crate::layout::{ }; use crate::realize::{Behave, Behaviour}; use crate::text::TextElem; -use crate::util::Numeric; +use crate::utils::Numeric; /// Separates a region into multiple equally sized columns. /// diff --git a/crates/typst/src/layout/container.rs b/crates/typst/src/layout/container.rs index fe6e056a..31699197 100644 --- a/crates/typst/src/layout/container.rs +++ b/crates/typst/src/layout/container.rs @@ -7,7 +7,7 @@ use crate::layout::{ Abs, Axes, Corners, Em, Fr, Fragment, Frame, FrameKind, LayoutMultiple, Length, Ratio, Regions, Rel, Sides, Size, Spacing, VElem, }; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::{clip_rect, Paint, Stroke}; /// An inline-level container that sizes content. diff --git a/crates/typst/src/layout/corners.rs b/crates/typst/src/layout/corners.rs index 3a2262bf..4b90854a 100644 --- a/crates/typst/src/layout/corners.rs +++ b/crates/typst/src/layout/corners.rs @@ -6,7 +6,7 @@ use crate::foundations::{ StyleChain, Value, }; use crate::layout::Side; -use crate::util::Get; +use crate::utils::Get; /// A container with components for the four corners of a rectangle. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] diff --git a/crates/typst/src/layout/em.rs b/crates/typst/src/layout/em.rs index 6a44ebb5..7aa5dfcf 100644 --- a/crates/typst/src/layout/em.rs +++ b/crates/typst/src/layout/em.rs @@ -7,7 +7,7 @@ use ecow::EcoString; use crate::foundations::{cast, repr, Repr, Resolve, StyleChain, Value}; use crate::layout::Abs; use crate::text::TextElem; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; /// A length that is relative to the font size. /// @@ -105,7 +105,7 @@ impl Add for Em { } } -sub_impl!(Em - Em -> Em); +typst_utils::sub_impl!(Em - Em -> Em); impl Mul for Em { type Output = Self; @@ -139,10 +139,10 @@ impl Div for Em { } } -assign_impl!(Em += Em); -assign_impl!(Em -= Em); -assign_impl!(Em *= f64); -assign_impl!(Em /= f64); +typst_utils::assign_impl!(Em += Em); +typst_utils::assign_impl!(Em -= Em); +typst_utils::assign_impl!(Em *= f64); +typst_utils::assign_impl!(Em /= f64); impl Sum for Em { fn sum>(iter: I) -> Self { diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs index 1e09ed83..6f3b8f16 100644 --- a/crates/typst/src/layout/flow.rs +++ b/crates/typst/src/layout/flow.rs @@ -18,7 +18,7 @@ use crate::layout::{ Rel, Size, Spacing, VElem, }; use crate::model::{FootnoteElem, FootnoteEntry, ParElem}; -use crate::util::Numeric; +use crate::utils::Numeric; /// Arranges spacing, paragraphs and block-level elements into a flow. /// diff --git a/crates/typst/src/layout/fr.rs b/crates/typst/src/layout/fr.rs index 5a42ce6f..a61daf1e 100644 --- a/crates/typst/src/layout/fr.rs +++ b/crates/typst/src/layout/fr.rs @@ -6,7 +6,7 @@ use ecow::EcoString; use crate::foundations::{repr, ty, Repr}; use crate::layout::Abs; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; /// Defines how the remaining space in a layout is distributed. /// @@ -99,7 +99,7 @@ impl Add for Fr { } } -sub_impl!(Fr - Fr -> Fr); +typst_utils::sub_impl!(Fr - Fr -> Fr); impl Mul for Fr { type Output = Self; @@ -133,10 +133,10 @@ impl Div for Fr { } } -assign_impl!(Fr += Fr); -assign_impl!(Fr -= Fr); -assign_impl!(Fr *= f64); -assign_impl!(Fr /= f64); +typst_utils::assign_impl!(Fr += Fr); +typst_utils::assign_impl!(Fr -= Fr); +typst_utils::assign_impl!(Fr *= f64); +typst_utils::assign_impl!(Fr /= f64); impl Sum for Fr { fn sum>(iter: I) -> Self { diff --git a/crates/typst/src/layout/frame.rs b/crates/typst/src/layout/frame.rs index 61e2d65f..be1a7cb3 100644 --- a/crates/typst/src/layout/frame.rs +++ b/crates/typst/src/layout/frame.rs @@ -11,7 +11,7 @@ use crate::layout::{ }; use crate::syntax::Span; use crate::text::TextItem; -use crate::util::{LazyHash, Numeric}; +use crate::utils::{LazyHash, Numeric}; use crate::visualize::{ ellipse, styled_rect, Color, FixedStroke, Geometry, Image, Paint, Path, Shape, }; diff --git a/crates/typst/src/layout/grid/cells.rs b/crates/typst/src/layout/grid/cells.rs index 3aef5934..2d3cc556 100644 --- a/crates/typst/src/layout/grid/cells.rs +++ b/crates/typst/src/layout/grid/cells.rs @@ -19,7 +19,7 @@ use crate::layout::{ Sides, Sizing, }; use crate::syntax::Span; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::visualize::{Paint, Stroke}; /// A value that can be configured per cell. diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs index 66d273ff..2f4adbe4 100644 --- a/crates/typst/src/layout/grid/layout.rs +++ b/crates/typst/src/layout/grid/layout.rs @@ -15,7 +15,7 @@ use crate::layout::{ }; use crate::syntax::Span; use crate::text::TextElem; -use crate::util::{MaybeReverseIter, Numeric}; +use crate::utils::{MaybeReverseIter, Numeric}; use crate::visualize::Geometry; /// Performs grid layout. diff --git a/crates/typst/src/layout/grid/lines.rs b/crates/typst/src/layout/grid/lines.rs index d7eea532..469f5305 100644 --- a/crates/typst/src/layout/grid/lines.rs +++ b/crates/typst/src/layout/grid/lines.rs @@ -603,7 +603,7 @@ mod test { use super::*; use crate::foundations::Content; use crate::layout::{Axes, Cell, Sides, Sizing}; - use crate::util::NonZeroExt; + use crate::utils::NonZeroExt; fn sample_cell() -> Cell { Cell { diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs index 09990ee1..4b6829c0 100644 --- a/crates/typst/src/layout/grid/mod.rs +++ b/crates/typst/src/layout/grid/mod.rs @@ -28,7 +28,7 @@ use crate::layout::{ use crate::model::{TableCell, TableFooter, TableHLine, TableHeader, TableVLine}; use crate::syntax::Span; use crate::text::TextElem; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::visualize::{Paint, Stroke}; /// Arranges content in a grid. diff --git a/crates/typst/src/layout/grid/rowspans.rs b/crates/typst/src/layout/grid/rowspans.rs index 0b32e4ad..6ee3fe8d 100644 --- a/crates/typst/src/layout/grid/rowspans.rs +++ b/crates/typst/src/layout/grid/rowspans.rs @@ -6,7 +6,7 @@ use crate::foundations::Resolve; use crate::layout::{ Abs, Axes, Cell, Frame, GridLayouter, LayoutMultiple, Point, Regions, Size, Sizing, }; -use crate::util::MaybeReverseIter; +use crate::utils::MaybeReverseIter; /// All information needed to layout a single rowspan. pub(super) struct Rowspan { diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs index 77408c7a..0b73eef6 100644 --- a/crates/typst/src/layout/inline/mod.rs +++ b/crates/typst/src/layout/inline/mod.rs @@ -25,7 +25,7 @@ use crate::syntax::Span; use crate::text::{ Lang, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, SpaceElem, TextElem, }; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::World; /// Layouts content inline. diff --git a/crates/typst/src/layout/inline/shaping.rs b/crates/typst/src/layout/inline/shaping.rs index aa30688b..ff13f776 100644 --- a/crates/typst/src/layout/inline/shaping.rs +++ b/crates/typst/src/layout/inline/shaping.rs @@ -18,7 +18,7 @@ use crate::text::{ decorate, families, features, variant, Font, FontVariant, Glyph, Lang, Region, TextElem, TextItem, }; -use crate::util::SliceExt; +use crate::utils::SliceExt; use crate::World; /// The result of shaping text. diff --git a/crates/typst/src/layout/length.rs b/crates/typst/src/layout/length.rs index 0e6176c0..8c883a6a 100644 --- a/crates/typst/src/layout/length.rs +++ b/crates/typst/src/layout/length.rs @@ -9,7 +9,7 @@ use crate::diag::{At, Hint, HintedStrResult, SourceResult}; use crate::foundations::{func, scope, ty, Context, Fold, Repr, Resolve, StyleChain}; use crate::layout::{Abs, Em}; use crate::syntax::Span; -use crate::util::Numeric; +use crate::utils::Numeric; /// A size or distance, possibly expressed with contextual units. /// @@ -227,7 +227,7 @@ impl Add for Length { } } -sub_impl!(Length - Length -> Length); +typst_utils::sub_impl!(Length - Length -> Length); impl Mul for Length { type Output = Self; @@ -253,10 +253,10 @@ impl Div for Length { } } -assign_impl!(Length += Length); -assign_impl!(Length -= Length); -assign_impl!(Length *= f64); -assign_impl!(Length /= f64); +typst_utils::assign_impl!(Length += Length); +typst_utils::assign_impl!(Length -= Length); +typst_utils::assign_impl!(Length *= f64); +typst_utils::assign_impl!(Length /= f64); impl Resolve for Length { type Output = Abs; diff --git a/crates/typst/src/layout/page.rs b/crates/typst/src/layout/page.rs index 85a88e1b..dae9293b 100644 --- a/crates/typst/src/layout/page.rs +++ b/crates/typst/src/layout/page.rs @@ -21,7 +21,7 @@ use crate::layout::{ use crate::model::Numbering; use crate::text::TextElem; -use crate::util::{NonZeroExt, Numeric, Scalar}; +use crate::utils::{NonZeroExt, Numeric, Scalar}; use crate::visualize::Paint; /// Layouts its child onto one or multiple pages. diff --git a/crates/typst/src/layout/point.rs b/crates/typst/src/layout/point.rs index bcc05c2b..172b6021 100644 --- a/crates/typst/src/layout/point.rs +++ b/crates/typst/src/layout/point.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::{Add, Div, Mul, Neg}; use crate::layout::{Abs, Axis, Size, Transform}; -use crate::util::{Get, Numeric}; +use crate::utils::{Get, Numeric}; /// A point in 2D. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] @@ -135,7 +135,7 @@ impl Add for Point { } } -sub_impl!(Point - Point -> Point); +typst_utils::sub_impl!(Point - Point -> Point); impl Mul for Point { type Output = Self; @@ -161,7 +161,7 @@ impl Div for Point { } } -assign_impl!(Point += Point); -assign_impl!(Point -= Point); -assign_impl!(Point *= f64); -assign_impl!(Point /= f64); +typst_utils::assign_impl!(Point += Point); +typst_utils::assign_impl!(Point -= Point); +typst_utils::assign_impl!(Point *= f64); +typst_utils::assign_impl!(Point /= f64); diff --git a/crates/typst/src/layout/ratio.rs b/crates/typst/src/layout/ratio.rs index 8b4e72d4..2d791f2d 100644 --- a/crates/typst/src/layout/ratio.rs +++ b/crates/typst/src/layout/ratio.rs @@ -4,7 +4,7 @@ use std::ops::{Add, Div, Mul, Neg}; use ecow::EcoString; use crate::foundations::{repr, ty, Repr}; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; /// A ratio of a whole. /// @@ -96,7 +96,7 @@ impl Add for Ratio { } } -sub_impl!(Ratio - Ratio -> Ratio); +typst_utils::sub_impl!(Ratio - Ratio -> Ratio); impl Mul for Ratio { type Output = Self; @@ -146,8 +146,8 @@ impl Div for f64 { } } -assign_impl!(Ratio += Ratio); -assign_impl!(Ratio -= Ratio); -assign_impl!(Ratio *= Ratio); -assign_impl!(Ratio *= f64); -assign_impl!(Ratio /= f64); +typst_utils::assign_impl!(Ratio += Ratio); +typst_utils::assign_impl!(Ratio -= Ratio); +typst_utils::assign_impl!(Ratio *= Ratio); +typst_utils::assign_impl!(Ratio *= f64); +typst_utils::assign_impl!(Ratio /= f64); diff --git a/crates/typst/src/layout/rel.rs b/crates/typst/src/layout/rel.rs index ed367588..2af63ba3 100644 --- a/crates/typst/src/layout/rel.rs +++ b/crates/typst/src/layout/rel.rs @@ -6,7 +6,7 @@ use ecow::{eco_format, EcoString}; use crate::foundations::{cast, ty, Fold, Repr, Resolve, StyleChain}; use crate::layout::{Abs, Em, Length, Ratio}; -use crate::util::Numeric; +use crate::utils::Numeric; /// A length in relation to some known length. /// diff --git a/crates/typst/src/layout/repeat.rs b/crates/typst/src/layout/repeat.rs index d74b39f7..e3a1cdab 100644 --- a/crates/typst/src/layout/repeat.rs +++ b/crates/typst/src/layout/repeat.rs @@ -4,7 +4,7 @@ use crate::foundations::{elem, Content, Packed, Resolve, StyleChain}; use crate::layout::{ Abs, AlignElem, Axes, Fragment, Frame, LayoutMultiple, Point, Regions, Size, }; -use crate::util::Numeric; +use crate::utils::Numeric; /// Repeats content to the available space. /// diff --git a/crates/typst/src/layout/sides.rs b/crates/typst/src/layout/sides.rs index 119d7f19..c75fab63 100644 --- a/crates/typst/src/layout/sides.rs +++ b/crates/typst/src/layout/sides.rs @@ -7,7 +7,7 @@ use crate::foundations::{ StyleChain, Value, }; use crate::layout::{Abs, Alignment, Axes, Axis, Corner, Rel, Size}; -use crate::util::Get; +use crate::utils::Get; /// A container with left, top, right and bottom components. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] diff --git a/crates/typst/src/layout/size.rs b/crates/typst/src/layout/size.rs index 92f26db0..8b5dc8fc 100644 --- a/crates/typst/src/layout/size.rs +++ b/crates/typst/src/layout/size.rs @@ -1,7 +1,7 @@ use std::ops::{Add, Div, Mul, Neg}; use crate::layout::{Abs, Axes, Point, Ratio}; -use crate::util::Numeric; +use crate::utils::Numeric; /// A size in 2D. pub type Size = Axes; @@ -54,7 +54,7 @@ impl Add for Size { } } -sub_impl!(Size - Size -> Size); +typst_utils::sub_impl!(Size - Size -> Size); impl Mul for Size { type Output = Self; @@ -80,7 +80,7 @@ impl Div for Size { } } -assign_impl!(Size -= Size); -assign_impl!(Size += Size); -assign_impl!(Size *= f64); -assign_impl!(Size /= f64); +typst_utils::assign_impl!(Size -= Size); +typst_utils::assign_impl!(Size += Size); +typst_utils::assign_impl!(Size *= f64); +typst_utils::assign_impl!(Size /= f64); diff --git a/crates/typst/src/layout/spacing.rs b/crates/typst/src/layout/spacing.rs index f3cb765b..776dfdb2 100644 --- a/crates/typst/src/layout/spacing.rs +++ b/crates/typst/src/layout/spacing.rs @@ -1,7 +1,7 @@ use crate::foundations::{cast, elem, Content, Packed, Resolve, StyleChain}; use crate::layout::{Abs, Em, Fr, Length, Ratio, Rel}; use crate::realize::{Behave, Behaviour}; -use crate::util::Numeric; +use crate::utils::Numeric; /// Inserts horizontal spacing into a paragraph. /// diff --git a/crates/typst/src/layout/stack.rs b/crates/typst/src/layout/stack.rs index 9c811ec3..33268bae 100644 --- a/crates/typst/src/layout/stack.rs +++ b/crates/typst/src/layout/stack.rs @@ -8,7 +8,7 @@ use crate::layout::{ Abs, AlignElem, Axes, Axis, Dir, FixedAlignment, Fr, Fragment, Frame, HElem, LayoutMultiple, Point, Regions, Size, Spacing, VElem, }; -use crate::util::{Get, Numeric}; +use crate::utils::{Get, Numeric}; /// Arranges content and spacing horizontally or vertically. /// diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs index f41fb605..bb869bfd 100644 --- a/crates/typst/src/lib.rs +++ b/crates/typst/src/lib.rs @@ -37,8 +37,6 @@ extern crate self as typst; -#[macro_use] -pub mod util; pub mod diag; pub mod engine; pub mod eval; @@ -55,6 +53,8 @@ pub mod visualize; #[doc(inline)] pub use typst_syntax as syntax; +#[doc(inline)] +pub use typst_utils as utils; use std::collections::HashSet; use std::ops::{Deref, Range}; @@ -168,7 +168,7 @@ fn typeset( fn deduplicate(mut diags: EcoVec) -> EcoVec { let mut unique = HashSet::new(); diags.retain(|diag| { - let hash = crate::util::hash128(&(&diag.span, &diag.message)); + let hash = crate::utils::hash128(&(&diag.span, &diag.message)); unique.insert(hash) }); diags diff --git a/crates/typst/src/math/equation.rs b/crates/typst/src/math/equation.rs index 18ef5150..eb3fcc4c 100644 --- a/crates/typst/src/math/equation.rs +++ b/crates/typst/src/math/equation.rs @@ -21,7 +21,7 @@ use crate::syntax::Span; use crate::text::{ families, variant, Font, FontFamily, FontList, FontWeight, LocalName, TextElem, }; -use crate::util::{NonZeroExt, Numeric}; +use crate::utils::{NonZeroExt, Numeric}; use crate::World; /// A mathematical equation. diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs index 83d50dd6..88122a57 100644 --- a/crates/typst/src/math/matrix.rs +++ b/crates/typst/src/math/matrix.rs @@ -14,7 +14,7 @@ use crate::math::{ }; use crate::syntax::{Span, Spanned}; use crate::text::TextElem; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::{FixedStroke, Geometry, LineCap, Shape, Stroke}; const DEFAULT_ROW_GAP: Em = Em::new(0.5); diff --git a/crates/typst/src/math/style.rs b/crates/typst/src/math/style.rs index 332ce586..2b278187 100644 --- a/crates/typst/src/math/style.rs +++ b/crates/typst/src/math/style.rs @@ -2,7 +2,7 @@ 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; +use crate::utils::LazyHash; /// Bold font style in math. /// diff --git a/crates/typst/src/model/bibliography.rs b/crates/typst/src/model/bibliography.rs index 9fcaad06..502a102b 100644 --- a/crates/typst/src/model/bibliography.rs +++ b/crates/typst/src/model/bibliography.rs @@ -40,7 +40,7 @@ use crate::syntax::{Span, Spanned}; use crate::text::{ FontStyle, Lang, LocalName, Region, SubElem, SuperElem, TextElem, WeightDelta, }; -use crate::util::{LazyHash, NonZeroExt, PicoStr}; +use crate::utils::{LazyHash, NonZeroExt, PicoStr}; use crate::World; /// A bibliography / reference listing. @@ -357,7 +357,7 @@ impl Bibliography { Ok(Bibliography { map: Arc::new(map), - hash: crate::util::hash128(data), + hash: crate::utils::hash128(data), }) } diff --git a/crates/typst/src/model/figure.rs b/crates/typst/src/model/figure.rs index 1f8c5759..30708711 100644 --- a/crates/typst/src/model/figure.rs +++ b/crates/typst/src/model/figure.rs @@ -19,7 +19,7 @@ use crate::layout::{ }; use crate::model::{Numbering, NumberingPattern, Outlinable, Refable, Supplement}; use crate::text::{Lang, Region, TextElem}; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::visualize::ImageElem; /// A figure with an optional caption. diff --git a/crates/typst/src/model/footnote.rs b/crates/typst/src/model/footnote.rs index 4945ebb1..67c2260d 100644 --- a/crates/typst/src/model/footnote.rs +++ b/crates/typst/src/model/footnote.rs @@ -11,7 +11,7 @@ use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Location}; use crate::layout::{Abs, Em, HElem, Length, Ratio}; use crate::model::{Destination, Numbering, NumberingPattern, ParElem}; use crate::text::{SuperElem, TextElem, TextSize}; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::visualize::{LineElem, Stroke}; /// A footnote. diff --git a/crates/typst/src/model/heading.rs b/crates/typst/src/model/heading.rs index 0744687e..c9389b38 100644 --- a/crates/typst/src/model/heading.rs +++ b/crates/typst/src/model/heading.rs @@ -12,7 +12,7 @@ use crate::layout::{ }; use crate::model::{Numbering, Outlinable, ParElem, Refable, Supplement}; use crate::text::{FontWeight, LocalName, SpaceElem, TextElem, TextSize}; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; /// A section heading. /// diff --git a/crates/typst/src/model/outline.rs b/crates/typst/src/model/outline.rs index 2cf574ce..09047285 100644 --- a/crates/typst/src/model/outline.rs +++ b/crates/typst/src/model/outline.rs @@ -16,7 +16,7 @@ use crate::model::{ }; use crate::syntax::Span; use crate::text::{LinebreakElem, LocalName, SpaceElem, TextElem}; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; /// A table of contents, figures, or other elements. /// diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs index 3a0c47c1..4b93517d 100644 --- a/crates/typst/src/model/table.rs +++ b/crates/typst/src/model/table.rs @@ -17,7 +17,7 @@ use crate::layout::{ use crate::model::Figurable; use crate::syntax::Span; use crate::text::{LocalName, TextElem}; -use crate::util::NonZeroExt; +use crate::utils::NonZeroExt; use crate::visualize::{Paint, Stroke}; /// A table of items. diff --git a/crates/typst/src/model/terms.rs b/crates/typst/src/model/terms.rs index 983012bd..84ae7730 100644 --- a/crates/typst/src/model/terms.rs +++ b/crates/typst/src/model/terms.rs @@ -9,7 +9,7 @@ use crate::layout::{ }; use crate::model::ParElem; use crate::text::TextElem; -use crate::util::Numeric; +use crate::utils::Numeric; /// A list of terms and their descriptions. /// diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs index da06159b..f5fcf1cb 100644 --- a/crates/typst/src/realize/process.rs +++ b/crates/typst/src/realize/process.rs @@ -11,7 +11,7 @@ use crate::foundations::{ }; use crate::introspection::{Locatable, Meta, MetaElem}; use crate::text::TextElem; -use crate::util::{hash128, BitSet}; +use crate::utils::{hash128, BitSet}; /// What to do with an element when encountering it during realization. struct Verdict<'a> { diff --git a/crates/typst/src/text/lang.rs b/crates/typst/src/text/lang.rs index a67fa33a..67df0c6e 100644 --- a/crates/typst/src/text/lang.rs +++ b/crates/typst/src/text/lang.rs @@ -277,7 +277,7 @@ fn lang_str(lang: Lang, region: Option) -> EcoString { #[cfg(test)] mod tests { use super::*; - use crate::util::option_eq; + use crate::utils::option_eq; #[test] fn test_region_option_eq() { diff --git a/crates/typst/src/util/bitset.rs b/crates/typst/src/util/bitset.rs deleted file mode 100644 index cbac7a1e..00000000 --- a/crates/typst/src/util/bitset.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::fmt::{self, Debug, Formatter}; - -/// Efficiently stores a set of numbers which are expected to be very small -/// (< 32/64 depending on the architecture). -/// -/// Inserting a very small value is very cheap while inserting a large one may -/// be very expensive. -#[derive(Clone, PartialEq, Hash)] -pub struct BitSet { - /// Used to store values < BITS. - low: usize, - /// Used to store values > BITS. We have the extra `Box` to keep the memory - /// size of the `BitSet` down. - #[allow(clippy::box_collection)] - hi: Option>>, -} - -/// The number of bits per chunk. -const BITS: usize = usize::BITS as usize; - -impl BitSet { - /// Creates a new empty bit set. - pub fn new() -> Self { - Self { low: 0, hi: None } - } - - /// Inserts a number into the set. - pub fn insert(&mut self, value: usize) { - if value < BITS { - self.low |= 1 << value; - } else { - let chunk = value / BITS - 1; - let within = value % BITS; - let vec = self.hi.get_or_insert_with(Default::default); - if chunk >= vec.len() { - vec.resize(chunk + 1, 0); - } - vec[chunk] |= 1 << within; - } - } - - /// Whether a number is present in the set. - pub fn contains(&self, value: usize) -> bool { - if value < BITS { - (self.low & (1 << value)) != 0 - } else { - let Some(hi) = &self.hi else { return false }; - let chunk = value / BITS - 1; - let within = value % BITS; - let Some(bits) = hi.get(chunk) else { return false }; - (bits & (1 << within)) != 0 - } - } -} - -impl Default for BitSet { - fn default() -> Self { - Self::new() - } -} - -impl Debug for BitSet { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let mut list = f.debug_list(); - let chunks = 1 + self.hi.as_ref().map_or(0, |v| v.len()); - for v in 0..chunks * BITS { - if self.contains(v) { - list.entry(&v); - } - } - list.finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bitset() { - let mut set = BitSet::new(); - assert!(!set.contains(0)); - assert!(!set.contains(5)); - set.insert(0); - set.insert(1); - set.insert(5); - set.insert(64); - set.insert(105); - set.insert(208); - assert!(set.contains(0)); - assert!(set.contains(1)); - assert!(!set.contains(2)); - assert!(set.contains(5)); - assert!(!set.contains(63)); - assert!(set.contains(64)); - assert!(!set.contains(65)); - assert!(!set.contains(104)); - assert!(set.contains(105)); - assert!(!set.contains(106)); - assert!(set.contains(208)); - assert!(!set.contains(209)); - assert_eq!(format!("{set:?}"), "[0, 1, 5, 64, 105, 208]"); - } -} diff --git a/crates/typst/src/util/deferred.rs b/crates/typst/src/util/deferred.rs deleted file mode 100644 index 46cd6e88..00000000 --- a/crates/typst/src/util/deferred.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::sync::Arc; - -use once_cell::sync::OnceCell; - -/// A value that is lazily executed on another thread. -/// -/// Execution will be started in the background and can be waited on. -pub struct Deferred(Arc>); - -impl Deferred { - /// Creates a new deferred value. - /// - /// The closure will be called on a secondary thread such that the value - /// can be initialized in parallel. - pub fn new(f: F) -> Self - where - F: FnOnce() -> T + Send + Sync + 'static, - { - let inner = Arc::new(OnceCell::new()); - let cloned = Arc::clone(&inner); - rayon::spawn(move || { - // Initialize the value if it hasn't been initialized yet. - // We do this to avoid panicking in case it was set externally. - cloned.get_or_init(f); - }); - Self(inner) - } - - /// Waits on the value to be initialized. - /// - /// If the value has already been initialized, this will return - /// immediately. Otherwise, this will block until the value is - /// initialized in another thread. - pub fn wait(&self) -> &T { - // Fast path if the value is already available. We don't want to yield - // to rayon in that case. - if let Some(value) = self.0.get() { - return value; - } - - // Ensure that we yield to give the deferred value a chance to compute - // single-threaded platforms (for WASM compatibility). - while let Some(rayon::Yield::Executed) = rayon::yield_now() {} - - self.0.wait() - } -} - -impl Clone for Deferred { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } -} diff --git a/crates/typst/src/util/fat.rs b/crates/typst/src/util/fat.rs deleted file mode 100644 index d3c9bb20..00000000 --- a/crates/typst/src/util/fat.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Fat pointer handling. -//! -//! This assumes the memory representation of fat pointers. Although it is not -//! guaranteed by Rust, it's improbable that it will change. Still, when the -//! pointer metadata APIs are stable, we should definitely move to them: -//! - -use std::alloc::Layout; -use std::mem; - -/// Create a fat pointer from a data address and a vtable address. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. The data address must point -/// to a value whose type implements the trait of `T` and the `vtable` must have -/// been extracted with [`vtable`]. -#[track_caller] -pub unsafe fn from_raw_parts(data: *const (), vtable: *const ()) -> *const T { - let fat = FatPointer { data, vtable }; - debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); - mem::transmute_copy::(&fat) -} - -/// Create a mutable fat pointer from a data address and a vtable address. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. The data address must point -/// to a value whose type implements the trait of `T` and the `vtable` must have -/// been extracted with [`vtable`]. -#[track_caller] -pub unsafe fn from_raw_parts_mut(data: *mut (), vtable: *const ()) -> *mut T { - let fat = FatPointer { data, vtable }; - debug_assert_eq!(Layout::new::<*mut T>(), Layout::new::()); - mem::transmute_copy::(&fat) -} - -/// Extract the address to a trait object's vtable. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. -#[track_caller] -pub unsafe fn vtable(ptr: *const T) -> *const () { - debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); - mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable -} - -/// The memory representation of a trait object pointer. -/// -/// Although this is not guaranteed by Rust, it's improbable that it will -/// change. -#[repr(C)] -struct FatPointer { - data: *const (), - vtable: *const (), -} diff --git a/crates/typst/src/util/hash.rs b/crates/typst/src/util/hash.rs deleted file mode 100644 index 2b6c5857..00000000 --- a/crates/typst/src/util/hash.rs +++ /dev/null @@ -1,161 +0,0 @@ -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`, 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 { - /// The hash for the value. - hash: AtomicU128, - /// The underlying value. - value: T, -} - -impl Default for LazyHash { - #[inline] - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl LazyHash { - /// 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 LazyHash { - /// 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(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 Hash for LazyHash { - #[inline] - fn hash(&self, state: &mut H) { - state.write_u128(self.get_or_set_hash()); - } -} - -impl From for LazyHash { - #[inline] - fn from(value: T) -> Self { - Self::new(value) - } -} - -impl Eq for LazyHash {} - -impl PartialEq for LazyHash { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.get_or_set_hash() == other.get_or_set_hash() - } -} - -impl Deref for LazyHash { - type Target = T; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.value - } -} - -impl DerefMut for LazyHash { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - self.reset_hash(); - &mut self.value - } -} - -impl Clone for LazyHash { - fn clone(&self) -> Self { - Self { - hash: AtomicU128::new(self.hash()), - value: self.value.clone(), - } - } -} - -impl Debug for LazyHash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.value.fmt(f) - } -} diff --git a/crates/typst/src/util/macros.rs b/crates/typst/src/util/macros.rs deleted file mode 100644 index b1b50e22..00000000 --- a/crates/typst/src/util/macros.rs +++ /dev/null @@ -1,47 +0,0 @@ -/// Implement the `Sub` trait based on existing `Neg` and `Add` impls. -macro_rules! sub_impl { - ($a:ident - $b:ident -> $c:ident) => { - impl std::ops::Sub<$b> for $a { - type Output = $c; - - fn sub(self, other: $b) -> $c { - self + -other - } - } - }; -} - -/// Implement an assign trait based on an existing non-assign trait. -macro_rules! assign_impl { - ($a:ident += $b:ident) => { - impl std::ops::AddAssign<$b> for $a { - fn add_assign(&mut self, other: $b) { - *self = *self + other; - } - } - }; - - ($a:ident -= $b:ident) => { - impl std::ops::SubAssign<$b> for $a { - fn sub_assign(&mut self, other: $b) { - *self = *self - other; - } - } - }; - - ($a:ident *= $b:ident) => { - impl std::ops::MulAssign<$b> for $a { - fn mul_assign(&mut self, other: $b) { - *self = *self * other; - } - } - }; - - ($a:ident /= $b:ident) => { - impl std::ops::DivAssign<$b> for $a { - fn div_assign(&mut self, other: $b) { - *self = *self / other; - } - } - }; -} diff --git a/crates/typst/src/util/mod.rs b/crates/typst/src/util/mod.rs deleted file mode 100644 index 802399fb..00000000 --- a/crates/typst/src/util/mod.rs +++ /dev/null @@ -1,245 +0,0 @@ -//! Utilities. - -pub mod fat; - -#[macro_use] -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; - -use std::fmt::{Debug, Formatter}; -use std::hash::Hash; -use std::iter::{Chain, Flatten, Rev}; -use std::num::NonZeroUsize; -use std::ops::{Add, Deref, Div, Mul, Neg, Sub}; -use std::sync::Arc; - -use siphasher::sip128::{Hasher128, SipHasher13}; - -/// Turn a closure into a struct implementing [`Debug`]. -pub fn debug(f: F) -> impl Debug -where - F: Fn(&mut Formatter) -> std::fmt::Result, -{ - struct Wrapper(F); - - impl Debug for Wrapper - where - F: Fn(&mut Formatter) -> std::fmt::Result, - { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.0(f) - } - } - - Wrapper(f) -} - -/// Calculate a 128-bit siphash of a value. -pub fn hash128(value: &T) -> u128 { - let mut state = SipHasher13::new(); - value.hash(&mut state); - state.finish128().as_u128() -} - -/// An extra constant for [`NonZeroUsize`]. -pub trait NonZeroExt { - /// The number `1`. - const ONE: Self; -} - -impl NonZeroExt for NonZeroUsize { - const ONE: Self = match Self::new(1) { - Some(v) => v, - None => unreachable!(), - }; -} - -/// Extra methods for [`Arc`]. -pub trait ArcExt { - /// Takes the inner value if there is exactly one strong reference and - /// clones it otherwise. - fn take(self) -> T; -} - -impl ArcExt for Arc { - fn take(self) -> T { - match Arc::try_unwrap(self) { - Ok(v) => v, - Err(rc) => (*rc).clone(), - } - } -} - -/// Extra methods for [`[T]`](slice). -pub trait SliceExt { - /// Split a slice into consecutive runs with the same key and yield for - /// each such run the key and the slice of elements with that key. - fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> - where - F: FnMut(&T) -> K, - K: PartialEq; -} - -impl SliceExt for [T] { - fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> { - GroupByKey { slice: self, f } - } -} - -/// This struct is created by [`SliceExt::group_by_key`]. -pub struct GroupByKey<'a, T, F> { - slice: &'a [T], - f: F, -} - -impl<'a, T, K, F> Iterator for GroupByKey<'a, T, F> -where - F: FnMut(&T) -> K, - K: PartialEq, -{ - type Item = (K, &'a [T]); - - fn next(&mut self) -> Option { - let mut iter = self.slice.iter(); - let key = (self.f)(iter.next()?); - let count = 1 + iter.take_while(|t| (self.f)(t) == key).count(); - let (head, tail) = self.slice.split_at(count); - self.slice = tail; - Some((key, head)) - } -} - -/// Adapter for reversing iterators conditionally. -pub trait MaybeReverseIter { - type RevIfIter; - - /// Reverse this iterator (apply .rev()) based on some condition. - fn rev_if(self, condition: bool) -> Self::RevIfIter - where - Self: Sized; -} - -impl MaybeReverseIter for I { - type RevIfIter = - Chain>, Flatten>>>; - - fn rev_if(self, condition: bool) -> Self::RevIfIter - where - Self: Sized, - { - let (maybe_self_iter, maybe_rev_iter) = - if condition { (None, Some(self.rev())) } else { (Some(self), None) }; - - maybe_self_iter - .into_iter() - .flatten() - .chain(maybe_rev_iter.into_iter().flatten()) - } -} - -/// Check if the [`Option`]-wrapped L is same to R. -pub fn option_eq(left: Option, other: R) -> bool -where - L: PartialEq, -{ - left.is_some_and(|v| v == other) -} - -/// A container around a static reference that is cheap to clone and hash. -#[derive(Debug)] -pub struct Static(pub &'static T); - -impl Deref for Static { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl Copy for Static {} - -impl Clone for Static { - fn clone(&self) -> Self { - *self - } -} - -impl Eq for Static {} - -impl PartialEq for Static { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self.0, other.0) - } -} - -impl Hash for Static { - fn hash(&self, state: &mut H) { - state.write_usize(self.0 as *const _ as _); - } -} - -/// Generic access to a structure's components. -pub trait Get { - /// The structure's component type. - type Component; - - /// Borrow the component for the specified index. - fn get_ref(&self, index: Index) -> &Self::Component; - - /// Borrow the component for the specified index mutably. - fn get_mut(&mut self, index: Index) -> &mut Self::Component; - - /// Convenience method for getting a copy of a component. - fn get(self, index: Index) -> Self::Component - where - Self: Sized, - Self::Component: Copy, - { - *self.get_ref(index) - } - - /// Convenience method for setting a component. - fn set(&mut self, index: Index, component: Self::Component) { - *self.get_mut(index) = component; - } -} - -/// A numeric type. -pub trait Numeric: - Sized - + Debug - + Copy - + PartialEq - + Neg - + Add - + Sub - + Mul - + Div -{ - /// The identity element for addition. - fn zero() -> Self; - - /// Whether `self` is zero. - fn is_zero(self) -> bool { - self == Self::zero() - } - - /// Whether `self` consists only of finite parts. - fn is_finite(self) -> bool; -} - -/// Round a float to two decimal places. -pub fn round_2(value: f64) -> f64 { - (value * 100.0).round() / 100.0 -} diff --git a/crates/typst/src/util/pico.rs b/crates/typst/src/util/pico.rs deleted file mode 100644 index 60af23ee..00000000 --- a/crates/typst/src/util/pico.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::cmp::Ordering; -use std::collections::HashMap; -use std::fmt::{self, Debug, Formatter}; -use std::sync::RwLock; - -use ecow::EcoString; -use once_cell::sync::Lazy; - -use crate::foundations::cast; - -/// The global string interner. -static INTERNER: Lazy> = - Lazy::new(|| RwLock::new(Interner { to_id: HashMap::new(), from_id: Vec::new() })); - -/// A string interner. -struct Interner { - to_id: HashMap<&'static str, PicoStr>, - from_id: Vec<&'static str>, -} - -/// An interned string. -/// -/// The API is purposefully kept small. This is because it might be relatively -/// slow to look up a string in the interner, so we want to avoid doing it -/// unnecessarily. For this reason, the user should use the [`PicoStr::resolve`] -/// method to get the underlying string, such that the lookup is done only once. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct PicoStr(u32); - -impl PicoStr { - /// Creates a new interned string. - pub fn new(string: &str) -> Self { - if let Some(&id) = INTERNER.read().unwrap().to_id.get(string) { - return id; - } - - let mut interner = INTERNER.write().unwrap(); - let num = interner.from_id.len().try_into().expect("out of string ids"); - - // Create a new entry forever by leaking the string. PicoStr is only - // used for strings that aren't created en masse, so it is okay. - let id = Self(num); - let string = Box::leak(string.to_string().into_boxed_str()); - interner.to_id.insert(string, id); - interner.from_id.push(string); - id - } - - /// Resolves the interned string. - pub fn resolve(&self) -> &'static str { - INTERNER.read().unwrap().from_id[self.0 as usize] - } -} - -cast! { - PicoStr, - self => self.resolve().into_value(), - v: EcoString => Self::new(&v), -} - -impl Debug for PicoStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.resolve().fmt(f) - } -} - -impl Ord for PicoStr { - fn cmp(&self, other: &Self) -> Ordering { - self.resolve().cmp(other.resolve()) - } -} - -impl PartialOrd for PicoStr { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl AsRef for PicoStr { - fn as_ref(&self) -> &str { - self.resolve() - } -} - -impl From<&str> for PicoStr { - fn from(value: &str) -> Self { - Self::new(value) - } -} - -impl From<&EcoString> for PicoStr { - fn from(value: &EcoString) -> Self { - Self::new(value) - } -} diff --git a/crates/typst/src/util/scalar.rs b/crates/typst/src/util/scalar.rs deleted file mode 100644 index 0fee9797..00000000 --- a/crates/typst/src/util/scalar.rs +++ /dev/null @@ -1,201 +0,0 @@ -use std::cmp::Ordering; -use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::iter::Sum; -use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, -}; - -use crate::util::Numeric; - -/// A 64-bit float that implements `Eq`, `Ord` and `Hash`. -/// -/// Panics if it's `NaN` during any of those operations. -#[derive(Default, Copy, Clone)] -pub struct Scalar(f64); - -impl Scalar { - /// The scalar containing `0.0`. - pub const ZERO: Self = Self(0.0); - - /// The scalar containing `1.0`. - pub const ONE: Self = Self(1.0); - - /// The scalar containing `f64::INFINITY`. - pub const INFINITY: Self = Self(f64::INFINITY); - - /// Creates a [`Scalar`] with the given value. - /// - /// If the value is NaN, then it is set to `0.0` in the result. - pub const fn new(x: f64) -> Self { - Self(if is_nan(x) { 0.0 } else { x }) - } - - /// Gets the value of this [`Scalar`]. - pub const fn get(self) -> f64 { - self.0 - } -} - -// We have to detect NaNs this way since `f64::is_nan` isn’t const -// on stable yet: -// ([tracking issue](https://github.com/rust-lang/rust/issues/57241)) -#[allow(clippy::unusual_byte_groupings)] -const fn is_nan(x: f64) -> bool { - // Safety: all bit patterns are valid for u64, and f64 has no padding bits. - // We cannot use `f64::to_bits` because it is not const. - let x_bits = unsafe { std::mem::transmute::(x) }; - (x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0 -} - -impl Numeric for Scalar { - fn zero() -> Self { - Self(0.0) - } - - fn is_finite(self) -> bool { - self.0.is_finite() - } -} - -impl Debug for Scalar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Eq for Scalar {} - -impl PartialEq for Scalar { - fn eq(&self, other: &Self) -> bool { - assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN"); - self.0 == other.0 - } -} - -impl PartialEq for Scalar { - fn eq(&self, other: &f64) -> bool { - self == &Self(*other) - } -} - -impl Ord for Scalar { - fn cmp(&self, other: &Self) -> Ordering { - self.0.partial_cmp(&other.0).expect("float is NaN") - } -} - -impl PartialOrd for Scalar { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Hash for Scalar { - fn hash(&self, state: &mut H) { - debug_assert!(!self.0.is_nan(), "float is NaN"); - self.0.to_bits().hash(state); - } -} - -impl From for Scalar { - fn from(float: f64) -> Self { - Self::new(float) - } -} - -impl From for f64 { - fn from(scalar: Scalar) -> Self { - scalar.0 - } -} - -impl Neg for Scalar { - type Output = Self; - - fn neg(self) -> Self::Output { - Self::new(-self.0) - } -} - -impl> Add for Scalar { - type Output = Self; - - fn add(self, rhs: T) -> Self::Output { - Self::new(self.0 + rhs.into().0) - } -} - -impl> AddAssign for Scalar { - fn add_assign(&mut self, rhs: T) { - *self = *self + rhs.into(); - } -} - -impl> Sub for Scalar { - type Output = Self; - - fn sub(self, rhs: T) -> Self::Output { - Self::new(self.0 - rhs.into().0) - } -} - -impl> SubAssign for Scalar { - fn sub_assign(&mut self, rhs: T) { - *self = *self - rhs.into(); - } -} - -impl> Mul for Scalar { - type Output = Self; - - fn mul(self, rhs: T) -> Self::Output { - Self::new(self.0 * rhs.into().0) - } -} - -impl> MulAssign for Scalar { - fn mul_assign(&mut self, rhs: T) { - *self = *self * rhs.into(); - } -} - -impl> Div for Scalar { - type Output = Self; - - fn div(self, rhs: T) -> Self::Output { - Self::new(self.0 / rhs.into().0) - } -} - -impl> DivAssign for Scalar { - fn div_assign(&mut self, rhs: T) { - *self = *self / rhs.into(); - } -} - -impl> Rem for Scalar { - type Output = Self; - - fn rem(self, rhs: T) -> Self::Output { - Self::new(self.0 % rhs.into().0) - } -} - -impl> RemAssign for Scalar { - fn rem_assign(&mut self, rhs: T) { - *self = *self % rhs.into(); - } -} - -impl Sum for Scalar { - fn sum>(iter: I) -> Self { - Self::new(iter.map(|s| s.0).sum()) - } -} - -impl<'a> Sum<&'a Self> for Scalar { - fn sum>(iter: I) -> Self { - Self::new(iter.map(|s| s.0).sum()) - } -} diff --git a/crates/typst/src/visualize/image/mod.rs b/crates/typst/src/visualize/image/mod.rs index c1715f7a..a5151e6e 100644 --- a/crates/typst/src/visualize/image/mod.rs +++ b/crates/typst/src/visualize/image/mod.rs @@ -27,7 +27,7 @@ use crate::loading::Readable; use crate::model::Figurable; use crate::syntax::{Span, Spanned}; use crate::text::{families, LocalName}; -use crate::util::LazyHash; +use crate::utils::LazyHash; use crate::visualize::Path; use crate::World; diff --git a/crates/typst/src/visualize/line.rs b/crates/typst/src/visualize/line.rs index 78b951d2..d84ea62f 100644 --- a/crates/typst/src/visualize/line.rs +++ b/crates/typst/src/visualize/line.rs @@ -4,7 +4,7 @@ use crate::foundations::{elem, Packed, StyleChain}; use crate::layout::{ Abs, Angle, Axes, Frame, FrameItem, LayoutSingle, Length, Regions, Rel, Size, }; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::{Geometry, Stroke}; /// A line from one point to another. diff --git a/crates/typst/src/visualize/pattern.rs b/crates/typst/src/visualize/pattern.rs index d180a81b..ed4d25ac 100644 --- a/crates/typst/src/visualize/pattern.rs +++ b/crates/typst/src/visualize/pattern.rs @@ -9,7 +9,7 @@ use crate::engine::Engine; use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain}; use crate::layout::{Abs, Axes, Frame, LayoutMultiple, Length, Regions, Size}; use crate::syntax::{Span, Spanned}; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::RelativeTo; use crate::World; diff --git a/crates/typst/src/visualize/polygon.rs b/crates/typst/src/visualize/polygon.rs index e0e18ddf..21db30a8 100644 --- a/crates/typst/src/visualize/polygon.rs +++ b/crates/typst/src/visualize/polygon.rs @@ -9,7 +9,7 @@ use crate::layout::{ Axes, Em, Frame, FrameItem, LayoutSingle, Length, Point, Regions, Rel, }; use crate::syntax::Span; -use crate::util::Numeric; +use crate::utils::Numeric; use crate::visualize::{FixedStroke, Geometry, Paint, Path, Shape, Stroke}; /// A closed polygon. diff --git a/crates/typst/src/visualize/shape.rs b/crates/typst/src/visualize/shape.rs index e37e5f90..88504121 100644 --- a/crates/typst/src/visualize/shape.rs +++ b/crates/typst/src/visualize/shape.rs @@ -8,7 +8,7 @@ use crate::layout::{ Point, Ratio, Regions, Rel, Sides, Size, }; use crate::syntax::Span; -use crate::util::Get; +use crate::utils::Get; use crate::visualize::{FixedStroke, Paint, Path, Stroke}; /// A rectangle with optional content. diff --git a/crates/typst/src/visualize/stroke.rs b/crates/typst/src/visualize/stroke.rs index 27157128..7d6909a5 100644 --- a/crates/typst/src/visualize/stroke.rs +++ b/crates/typst/src/visualize/stroke.rs @@ -6,7 +6,7 @@ use crate::foundations::{ Resolve, Smart, StyleChain, Value, }; use crate::layout::{Abs, Length}; -use crate::util::{Numeric, Scalar}; +use crate::utils::{Numeric, Scalar}; use crate::visualize::{Color, Gradient, Paint, Pattern}; /// Defines how to draw a line. diff --git a/docs/src/html.rs b/docs/src/html.rs index bb6204a3..e88f0d3d 100644 --- a/docs/src/html.rs +++ b/docs/src/html.rs @@ -429,7 +429,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { document.pages.truncate(1); } - let hash = typst::util::hash128(text); + let hash = typst::utils::hash128(text); resolver.example(hash, highlighted, &document) } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 19d9e5e9..3e29c0ff 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -101,7 +101,7 @@ fn test() { // // We use `par_bridge` instead of `par_iter` because the former // results in a stack overflow during PDF export. Probably related - // to `typst::util::Deferred` yielding. + // to `typst::utils::Deferred` yielding. tests.iter().par_bridge().for_each(|test| { logger.lock().start(test); let result = std::panic::catch_unwind(|| run::run(test)); -- cgit v1.2.3