Share allocations for singletons (#4794)

This commit is contained in:
Laurenz 2024-08-20 12:56:54 +02:00 committed by GitHub
parent a30b681251
commit 986d624b3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 92 additions and 47 deletions

View File

@ -1,15 +1,16 @@
use std::io::{self, IsTerminal, Write};
use codespan_reporting::term::termcolor;
use once_cell::sync::Lazy;
use termcolor::{ColorChoice, WriteColor};
use typst::utils::singleton;
use crate::ARGS;
/// Returns a handle to the optionally colored terminal output.
pub fn out() -> TermOut {
static OUTPUT: Lazy<TermOutInner> = Lazy::new(TermOutInner::new);
TermOut { inner: &OUTPUT }
TermOut {
inner: singleton!(TermOutInner, TermOutInner::new()),
}
}
/// The stuff that has to be shared between instances of [`TermOut`].

View File

@ -97,13 +97,12 @@ fn summarize_font_family<'a>(variants: impl Iterator<Item = &'a FontInfo>) -> Ec
#[cfg(test)]
mod tests {
use once_cell::sync::Lazy;
use typst::diag::{FileError, FileResult};
use typst::foundations::{Bytes, Datetime, Smart};
use typst::layout::{Abs, Margin, PageElem};
use typst::syntax::{FileId, Source};
use typst::text::{Font, FontBook, TextElem, TextSize};
use typst::utils::LazyHash;
use typst::utils::{singleton, LazyHash};
use typst::{Library, World};
/// A world for IDE testing.
@ -118,15 +117,16 @@ mod tests {
/// This is cheap because the shared base for all test runs is lazily
/// initialized just once.
pub fn new(text: &str) -> Self {
static BASE: Lazy<TestBase> = Lazy::new(TestBase::default);
let main = Source::detached(text);
Self { main, base: &*BASE }
Self {
main,
base: singleton!(TestBase, TestBase::default()),
}
}
/// The ID of the main file in a `TestWorld`.
pub fn main_id() -> FileId {
static ID: Lazy<FileId> = Lazy::new(|| Source::detached("").id());
*ID
*singleton!(FileId, Source::detached("").id())
}
}

View File

@ -25,6 +25,9 @@ use std::sync::Arc;
use siphasher::sip128::{Hasher128, SipHasher13};
#[doc(hidden)]
pub use once_cell;
/// Turn a closure into a struct implementing [`Debug`].
pub fn debug<F>(f: F) -> impl Debug
where

View File

@ -1,8 +1,18 @@
/// Create a lazy initialized, globally unique `'static` reference to a value.
#[macro_export]
macro_rules! singleton {
($ty:ty, $value:expr) => {{
static VALUE: $crate::once_cell::sync::Lazy<$ty> =
$crate::once_cell::sync::Lazy::new(|| $value);
&*VALUE
}};
}
/// 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 {
impl ::core::ops::Sub<$b> for $a {
type Output = $c;
fn sub(self, other: $b) -> $c {
@ -16,7 +26,7 @@ macro_rules! sub_impl {
#[macro_export]
macro_rules! assign_impl {
($a:ident += $b:ident) => {
impl std::ops::AddAssign<$b> for $a {
impl ::core::ops::AddAssign<$b> for $a {
fn add_assign(&mut self, other: $b) {
*self = *self + other;
}
@ -24,7 +34,7 @@ macro_rules! assign_impl {
};
($a:ident -= $b:ident) => {
impl std::ops::SubAssign<$b> for $a {
impl ::core::ops::SubAssign<$b> for $a {
fn sub_assign(&mut self, other: $b) {
*self = *self - other;
}
@ -32,7 +42,7 @@ macro_rules! assign_impl {
};
($a:ident *= $b:ident) => {
impl std::ops::MulAssign<$b> for $a {
impl ::core::ops::MulAssign<$b> for $a {
fn mul_assign(&mut self, other: $b) {
*self = *self * other;
}
@ -40,7 +50,7 @@ macro_rules! assign_impl {
};
($a:ident /= $b:ident) => {
impl std::ops::DivAssign<$b> for $a {
impl ::core::ops::DivAssign<$b> for $a {
fn div_assign(&mut self, other: $b) {
*self = *self / other;
}

View File

@ -83,7 +83,7 @@ impl Eval for ast::Space<'_> {
type Output = Content;
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
Ok(SpaceElem::new().pack())
Ok(SpaceElem::shared().clone())
}
}
@ -91,7 +91,7 @@ impl Eval for ast::Linebreak<'_> {
type Output = Content;
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
Ok(LinebreakElem::new().pack())
Ok(LinebreakElem::shared().clone())
}
}
@ -99,7 +99,7 @@ impl Eval for ast::Parbreak<'_> {
type Output = Content;
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
Ok(ParbreakElem::new().pack())
Ok(ParbreakElem::shared().clone())
}
}

View File

@ -39,7 +39,7 @@ impl Eval for ast::MathAlignPoint<'_> {
type Output = Content;
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
Ok(AlignPointElem::new().pack())
Ok(AlignPointElem::shared().clone())
}
}

View File

@ -12,7 +12,7 @@ use crate::foundations::{
Selector, Type, Value,
};
use crate::syntax::{ast, Span, SyntaxNode};
use crate::utils::{LazyHash, Static};
use crate::utils::{singleton, LazyHash, Static};
#[doc(inline)]
pub use typst_macros::func;
@ -216,11 +216,11 @@ impl Func {
/// Get details about the function's return type.
pub fn returns(&self) -> Option<&'static CastInfo> {
static CONTENT: Lazy<CastInfo> =
Lazy::new(|| CastInfo::Type(Type::of::<Content>()));
match &self.repr {
Repr::Native(native) => Some(&native.0.returns),
Repr::Element(_) => Some(&CONTENT),
Repr::Element(_) => {
Some(singleton!(CastInfo, CastInfo::Type(Type::of::<Content>())))
}
Repr::Closure(_) => None,
Repr::With(with) => with.0.returns(),
}

View File

@ -1,12 +1,20 @@
use crate::diag::SourceResult;
use crate::foundations::{elem, Packed, StyleChain};
use crate::foundations::{elem, Content, NativeElement, Packed, StyleChain};
use crate::layout::Abs;
use crate::math::{LayoutMath, MathContext, MathFragment, MathRun};
use crate::utils::singleton;
/// A math alignment point: `&`, `&&`.
#[elem(title = "Alignment Point", LayoutMath)]
pub struct AlignPointElem {}
impl AlignPointElem {
/// Get the globally shared alignment point element.
pub fn shared() -> &'static Content {
singleton!(Content, AlignPointElem::new().pack())
}
}
impl LayoutMath for Packed<AlignPointElem> {
fn layout_math(&self, ctx: &mut MathContext, _: StyleChain) -> SourceResult<()> {
ctx.push(MathFragment::Align);

View File

@ -331,7 +331,7 @@ impl Outlinable for Packed<HeadingElem> {
styles,
numbering,
)?;
content = numbers + SpaceElem::new().pack() + content;
content = numbers + SpaceElem::shared().clone() + content;
};
Ok(Some(content))

View File

@ -190,7 +190,7 @@ impl OutlineElem {
impl Show for Packed<OutlineElem> {
#[typst_macros::time(name = "outline", span = self.span())]
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut seq = vec![ParbreakElem::new().pack()];
let mut seq = vec![ParbreakElem::shared().clone()];
// Build the outline title.
if let Some(title) = self.title(styles).unwrap_or_else(|| {
Some(TextElem::packed(Self::local_name_in(styles)).spanned(self.span()))
@ -247,12 +247,12 @@ impl Show for Packed<OutlineElem> {
// Add the overridable outline entry, followed by a line break.
seq.push(entry.pack());
seq.push(LinebreakElem::new().pack());
seq.push(LinebreakElem::shared().clone());
ancestors.push(elem);
}
seq.push(ParbreakElem::new().pack());
seq.push(ParbreakElem::shared().clone());
Ok(Content::sequence(seq))
}
@ -325,13 +325,13 @@ impl OutlineIndent {
numbering,
)?;
hidden += numbers + SpaceElem::new().pack();
hidden += numbers + SpaceElem::shared().clone();
};
}
if !ancestors.is_empty() {
seq.push(HideElem::new(hidden).pack());
seq.push(SpaceElem::new().pack());
seq.push(SpaceElem::shared().clone());
}
}
@ -508,7 +508,7 @@ impl Show for Packed<OutlineEntry> {
// Add filler symbols between the section name and page number.
if let Some(filler) = self.fill() {
seq.push(SpaceElem::new().pack());
seq.push(SpaceElem::shared().clone());
seq.push(
BoxElem::new()
.with_body(Some(filler.clone()))
@ -516,7 +516,7 @@ impl Show for Packed<OutlineEntry> {
.pack()
.spanned(self.span()),
);
seq.push(SpaceElem::new().pack());
seq.push(SpaceElem::shared().clone());
} else {
seq.push(HElem::new(Fr::one().into()).pack());
}

View File

@ -7,6 +7,7 @@ use crate::foundations::{
};
use crate::layout::{Em, Length};
use crate::realize::StyleVec;
use crate::utils::singleton;
/// Arranges text, spacing and inline-level elements into a paragraph.
///
@ -150,9 +151,9 @@ impl Construct for ParElem {
let styles = Self::set(engine, args)?;
let body = args.expect::<Content>("body")?;
Ok(Content::sequence([
ParbreakElem::new().pack(),
ParbreakElem::shared().clone(),
body.styled_with_map(styles),
ParbreakElem::new().pack(),
ParbreakElem::shared().clone(),
]))
}
}
@ -197,4 +198,11 @@ pub enum Linebreaks {
#[elem(title = "Paragraph Break", Unlabellable)]
pub struct ParbreakElem {}
impl ParbreakElem {
/// Get the globally shared paragraph element.
pub fn shared() -> &'static Content {
singleton!(Content, ParbreakElem::new().pack())
}
}
impl Unlabellable for Packed<ParbreakElem> {}

View File

@ -189,7 +189,7 @@ impl Show for Packed<QuoteElem> {
.spanned(self.span());
if let Some(attribution) = self.attribution(styles).as_ref() {
let mut seq = vec![TextElem::packed('—'), SpaceElem::new().pack()];
let mut seq = vec![TextElem::packed('—'), SpaceElem::shared().clone()];
match attribution {
Attribution::Content(content) => {
@ -213,7 +213,7 @@ impl Show for Packed<QuoteElem> {
realized = PadElem::new(realized).pack();
} else if let Some(Attribution::Label(label)) = self.attribution(styles) {
realized += SpaceElem::new().pack()
realized += SpaceElem::shared().clone()
+ CiteElem::new(*label).pack().spanned(self.span());
}

View File

@ -1,5 +1,6 @@
use crate::foundations::{elem, Packed};
use crate::foundations::{elem, Content, NativeElement, Packed};
use crate::realize::{Behave, Behaviour};
use crate::utils::singleton;
/// Inserts a line break.
///
@ -37,6 +38,13 @@ pub struct LinebreakElem {
pub justify: bool,
}
impl LinebreakElem {
/// Get the globally shared linebreak element.
pub fn shared() -> &'static Content {
singleton!(Content, LinebreakElem::new().pack())
}
}
impl Behave for Packed<LinebreakElem> {
fn behaviour(&self) -> Behaviour {
Behaviour::Destructive

View File

@ -440,7 +440,7 @@ impl Show for Packed<RawElem> {
let mut seq = EcoVec::with_capacity((2 * lines.len()).saturating_sub(1));
for (i, line) in lines.iter().enumerate() {
if i != 0 {
seq.push(LinebreakElem::new().pack());
seq.push(LinebreakElem::shared().clone());
}
seq.push(line.clone().pack());

View File

@ -1,12 +1,22 @@
use ecow::EcoString;
use crate::foundations::{elem, Packed, PlainText, Repr, Unlabellable};
use crate::foundations::{
elem, Content, NativeElement, Packed, PlainText, Repr, Unlabellable,
};
use crate::realize::{Behave, Behaviour};
use crate::utils::singleton;
/// A text space.
#[elem(Behave, Unlabellable, PlainText, Repr)]
pub struct SpaceElem {}
impl SpaceElem {
/// Get the globally shared space element.
pub fn shared() -> &'static Content {
singleton!(Content, SpaceElem::new().pack())
}
}
impl Repr for SpaceElem {
fn repr(&self) -> EcoString {
"[ ]".into()

View File

@ -211,11 +211,7 @@ pub enum Color {
#[scope]
impl Color {
/// The module of preset color maps.
pub const MAP: fn() -> Module = || {
// Lazy to avoid re-allocating.
static MODULE: Lazy<Module> = Lazy::new(map);
MODULE.clone()
};
pub const MAP: fn() -> Module = || crate::utils::singleton!(Module, map()).clone();
pub const BLACK: Self = Self::Luma(Luma::new(0.0, 1.0));
pub const GRAY: Self = Self::Luma(Luma::new(0.6666666, 1.0));

View File

@ -5,14 +5,13 @@ use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use typst::diag::{bail, FileError, FileResult, StrResult};
use typst::foundations::{func, Bytes, Datetime, NoneValue, Repr, Smart, Value};
use typst::layout::{Abs, Margin, PageElem};
use typst::syntax::{FileId, Source};
use typst::text::{Font, FontBook, TextElem, TextSize};
use typst::utils::LazyHash;
use typst::utils::{singleton, LazyHash};
use typst::visualize::Color;
use typst::{Library, World};
@ -29,8 +28,10 @@ impl TestWorld {
/// This is cheap because the shared base for all test runs is lazily
/// initialized just once.
pub fn new(source: Source) -> Self {
static BASE: Lazy<TestBase> = Lazy::new(TestBase::default);
Self { main: source, base: &*BASE }
Self {
main: source,
base: singleton!(TestBase, TestBase::default()),
}
}
}