mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Switch to ecow
This commit is contained in:
parent
6e65ebf236
commit
a1d47695a2
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -304,6 +304,11 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecow"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/typst/ecow#36b624de42f12f90ebad3e0498d770e1700f2e90"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elsa"
|
name = "elsa"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -1166,6 +1171,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"comemo",
|
"comemo",
|
||||||
|
"ecow",
|
||||||
"flate2",
|
"flate2",
|
||||||
"if_chain",
|
"if_chain",
|
||||||
"image",
|
"image",
|
||||||
@ -1238,6 +1244,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"comemo",
|
"comemo",
|
||||||
"csv",
|
"csv",
|
||||||
|
"ecow",
|
||||||
"hypher",
|
"hypher",
|
||||||
"kurbo",
|
"kurbo",
|
||||||
"lipsum",
|
"lipsum",
|
||||||
@ -1569,7 +1576,8 @@ checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "xmp-writer"
|
name = "xmp-writer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/typst/xmp-writer#0bce4e38395877dad229ea4518d4f78038738155"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fd742bbbb930fc972b28bf66b7546dfbc7bb9a4c7924299df0ae6a5641fcadf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-front-matter"
|
name = "yaml-front-matter"
|
||||||
|
@ -16,6 +16,7 @@ typst-macros = { path = "macros" }
|
|||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytemuck = "1"
|
bytemuck = "1"
|
||||||
comemo = { git = "https://github.com/typst/comemo" }
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
|
ecow = { git = "https://github.com/typst/ecow" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
if_chain = "1"
|
if_chain = "1"
|
||||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
|
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
|
||||||
@ -40,7 +41,7 @@ unicode-segmentation = "1"
|
|||||||
unicode-xid = "0.2"
|
unicode-xid = "0.2"
|
||||||
unscanny = "0.1"
|
unscanny = "0.1"
|
||||||
usvg = { version = "0.22", default-features = false }
|
usvg = { version = "0.22", default-features = false }
|
||||||
xmp-writer = { git = "https://github.com/typst/xmp-writer" }
|
xmp-writer = "0.1"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug = 0
|
debug = 0
|
||||||
|
@ -13,6 +13,7 @@ bench = false
|
|||||||
typst = { path = ".." }
|
typst = { path = ".." }
|
||||||
comemo = { git = "https://github.com/typst/comemo" }
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
csv = "1"
|
csv = "1"
|
||||||
|
ecow = { git = "https://github.com/typst/ecow" }
|
||||||
hypher = "0.1"
|
hypher = "0.1"
|
||||||
kurbo = "0.8"
|
kurbo = "0.8"
|
||||||
lipsum = { git = "https://github.com/reknih/lipsum" }
|
lipsum = { git = "https://github.com/reknih/lipsum" }
|
||||||
|
@ -458,12 +458,12 @@ pub fn range(args: &mut Args) -> SourceResult<Value> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut x = start;
|
let mut x = start;
|
||||||
let mut seq = vec![];
|
let mut array = Array::new();
|
||||||
|
|
||||||
while x.cmp(&end) == 0.cmp(&step) {
|
while x.cmp(&end) == 0.cmp(&step) {
|
||||||
seq.push(Value::Int(x));
|
array.push(Value::Int(x));
|
||||||
x += step;
|
x += step;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Array(Array::from_vec(seq)))
|
Ok(Value::Array(array))
|
||||||
}
|
}
|
||||||
|
@ -86,15 +86,15 @@ pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut reader = builder.from_reader(data.as_slice());
|
let mut reader = builder.from_reader(data.as_slice());
|
||||||
let mut vec = vec![];
|
let mut array = Array::new();
|
||||||
|
|
||||||
for result in reader.records() {
|
for result in reader.records() {
|
||||||
let row = result.map_err(format_csv_error).at(span)?;
|
let row = result.map_err(format_csv_error).at(span)?;
|
||||||
let array = row.iter().map(|field| Value::Str(field.into())).collect();
|
let sub = row.iter().map(|field| Value::Str(field.into())).collect();
|
||||||
vec.push(Value::Array(array))
|
array.push(Value::Array(sub))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Array(Array::from_vec(vec)))
|
Ok(Value::Array(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The delimiter to use when parsing CSV files.
|
/// The delimiter to use when parsing CSV files.
|
||||||
|
@ -8,6 +8,8 @@ pub use std::num::NonZeroUsize;
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use comemo::{Track, Tracked, TrackedMut};
|
pub use comemo::{Track, Tracked, TrackedMut};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
pub use ecow::{format_eco, EcoString};
|
||||||
|
#[doc(no_inline)]
|
||||||
pub use typst::diag::{bail, error, At, SourceResult, StrResult};
|
pub use typst::diag::{bail, error, At, SourceResult, StrResult};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::doc::*;
|
pub use typst::doc::*;
|
||||||
@ -23,8 +25,6 @@ pub use typst::model::{
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::syntax::{Span, Spanned};
|
pub use typst::syntax::{Span, Spanned};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::util::{format_eco, EcoString};
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use typst::World;
|
pub use typst::World;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
@ -18,7 +18,6 @@ use std::borrow::Cow;
|
|||||||
|
|
||||||
use rustybuzz::Tag;
|
use rustybuzz::Tag;
|
||||||
use typst::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
|
use typst::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
|
||||||
use typst::util::EcoString;
|
|
||||||
|
|
||||||
use crate::layout::ParNode;
|
use crate::layout::ParNode;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use typst::model::SequenceNode;
|
use typst::model::SequenceNode;
|
||||||
use typst::util::EcoString;
|
|
||||||
|
|
||||||
use super::{variant, SpaceNode, TextNode, TextSize};
|
use super::{variant, SpaceNode, TextNode, TextSize};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -8,9 +8,9 @@ use std::str::Utf8Error;
|
|||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use crate::syntax::{ErrorPos, Span, Spanned};
|
use crate::syntax::{ErrorPos, Span, Spanned};
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Early-return with a [`SourceError`].
|
/// Early-return with a [`SourceError`].
|
||||||
@ -38,12 +38,14 @@ macro_rules! __error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
|
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
|
||||||
$crate::diag::error!($span, $crate::util::format_eco!($fmt, $($arg),+))
|
$crate::diag::error!($span, $crate::diag::format_eco!($fmt, $($arg),+))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use crate::__error as error;
|
pub use crate::__error as error;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use ecow::format_eco;
|
||||||
|
|
||||||
/// A result that can carry multiple source errors.
|
/// A result that can carry multiple source errors.
|
||||||
pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>;
|
pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>;
|
||||||
|
@ -5,6 +5,8 @@ use std::num::NonZeroUsize;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use crate::font::Font;
|
use crate::font::Font;
|
||||||
use crate::geom::{
|
use crate::geom::{
|
||||||
self, rounded_rect, Abs, Align, Axes, Color, Corners, Dir, Em, Geometry, Numeric,
|
self, rounded_rect, Abs, Align, Axes, Color, Corners, Dir, Em, Geometry, Numeric,
|
||||||
@ -14,7 +16,6 @@ use crate::image::Image;
|
|||||||
use crate::model::{
|
use crate::model::{
|
||||||
capable, dict, node, Content, Dict, Fold, StableId, StyleChain, Value,
|
capable, dict, node, Content, Dict, Fold, StableId, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// A finished document with metadata and page frames.
|
/// A finished document with metadata and page frames.
|
||||||
#[derive(Debug, Default, Clone, Hash)]
|
#[derive(Debug, Default, Clone, Hash)]
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use ecow::format_eco;
|
||||||
use pdf_writer::types::{CidFontType, FontFlags, SystemInfo, UnicodeCmap};
|
use pdf_writer::types::{CidFontType, FontFlags, SystemInfo, UnicodeCmap};
|
||||||
use pdf_writer::{Filter, Finish, Name, Rect, Str};
|
use pdf_writer::{Filter, Finish, Name, Rect, Str};
|
||||||
use ttf_parser::{name_id, GlyphId, Tag};
|
use ttf_parser::{name_id, GlyphId, Tag};
|
||||||
|
|
||||||
use super::{deflate, EmExt, PdfContext, RefExt};
|
use super::{deflate, EmExt, PdfContext, RefExt};
|
||||||
use crate::util::{format_eco, SliceExt};
|
use crate::util::SliceExt;
|
||||||
|
|
||||||
/// Embed all used fonts into the PDF.
|
/// Embed all used fonts into the PDF.
|
||||||
pub fn write_fonts(ctx: &mut PdfContext) {
|
pub fn write_fonts(ctx: &mut PdfContext) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use ecow::EcoString;
|
||||||
use pdf_writer::{Finish, Ref, TextStr};
|
use pdf_writer::{Finish, Ref, TextStr};
|
||||||
|
|
||||||
use super::{AbsExt, PdfContext, RefExt};
|
use super::{AbsExt, PdfContext, RefExt};
|
||||||
use crate::geom::{Abs, Point};
|
use crate::geom::{Abs, Point};
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// A heading in the outline panel.
|
/// A heading in the outline panel.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use ecow::format_eco;
|
||||||
use pdf_writer::types::{ActionType, AnnotationType, ColorSpaceOperand};
|
use pdf_writer::types::{ActionType, AnnotationType, ColorSpaceOperand};
|
||||||
use pdf_writer::writers::ColorSpace;
|
use pdf_writer::writers::ColorSpace;
|
||||||
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
|
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
|
||||||
@ -10,7 +11,6 @@ use crate::geom::{
|
|||||||
Transform,
|
Transform,
|
||||||
};
|
};
|
||||||
use crate::image::Image;
|
use crate::image::Image;
|
||||||
use crate::util::format_eco;
|
|
||||||
|
|
||||||
/// Construct page objects.
|
/// Construct page objects.
|
||||||
pub fn construct_pages(ctx: &mut PdfContext, frames: &[Frame]) {
|
pub fn construct_pages(ctx: &mut PdfContext, frames: &[Frame]) {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::collections::{BTreeSet, HashSet};
|
use std::collections::{BTreeSet, HashSet};
|
||||||
|
|
||||||
|
use ecow::{format_eco, EcoString};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
|
|
||||||
use super::{analyze_expr, analyze_import, plain_docs_sentence, summarize_font_family};
|
use super::{analyze_expr, analyze_import, plain_docs_sentence, summarize_font_family};
|
||||||
use crate::model::{methods_on, CastInfo, Scope, Value};
|
use crate::model::{methods_on, CastInfo, Scope, Value};
|
||||||
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind};
|
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind};
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Autocomplete a cursor position in a source file.
|
/// Autocomplete a cursor position in a source file.
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::sync::Arc;
|
|
||||||
|
use ecow::{format_eco, EcoString, EcoVec};
|
||||||
|
|
||||||
use super::{ops, Args, Func, Value, Vm};
|
use super::{ops, Args, Func, Value, Vm};
|
||||||
use crate::diag::{bail, At, SourceResult, StrResult};
|
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||||
use crate::util::{format_eco, ArcExt, EcoString};
|
|
||||||
|
|
||||||
/// Create a new [`Array`] from values.
|
/// Create a new [`Array`] from values.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! __array {
|
macro_rules! __array {
|
||||||
($value:expr; $count:expr) => {
|
($value:expr; $count:expr) => {
|
||||||
$crate::model::Array::from_vec(vec![$value.into(); $count])
|
$crate::model::Array::from_vec($crate::model::eco_vec![$value.into(); $count])
|
||||||
};
|
};
|
||||||
|
|
||||||
($($value:expr),* $(,)?) => {
|
($($value:expr),* $(,)?) => {
|
||||||
$crate::model::Array::from_vec(vec![$($value.into()),*])
|
$crate::model::Array::from_vec($crate::model::eco_vec![$($value.into()),*])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use crate::__array as array;
|
pub use crate::__array as array;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use ecow::eco_vec;
|
||||||
|
|
||||||
/// A reference counted array with value semantics.
|
/// A reference counted array with value semantics.
|
||||||
#[derive(Default, Clone, PartialEq, Hash)]
|
#[derive(Default, Clone, PartialEq, Hash)]
|
||||||
pub struct Array(Arc<Vec<Value>>);
|
pub struct Array(EcoVec<Value>);
|
||||||
|
|
||||||
impl Array {
|
impl Array {
|
||||||
/// Create a new, empty array.
|
/// Create a new, empty array.
|
||||||
@ -33,9 +35,9 @@ impl Array {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new array from a vector of values.
|
/// Create a new array from an eco vector of values.
|
||||||
pub fn from_vec(vec: Vec<Value>) -> Self {
|
pub fn from_vec(vec: EcoVec<Value>) -> Self {
|
||||||
Self(Arc::new(vec))
|
Self(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The length of the array.
|
/// The length of the array.
|
||||||
@ -50,7 +52,7 @@ impl Array {
|
|||||||
|
|
||||||
/// Mutably borrow the first value in the array.
|
/// Mutably borrow the first value in the array.
|
||||||
pub fn first_mut(&mut self) -> StrResult<&mut Value> {
|
pub fn first_mut(&mut self) -> StrResult<&mut Value> {
|
||||||
Arc::make_mut(&mut self.0).first_mut().ok_or_else(array_is_empty)
|
self.0.make_mut().first_mut().ok_or_else(array_is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The last value in the array.
|
/// The last value in the array.
|
||||||
@ -60,7 +62,7 @@ impl Array {
|
|||||||
|
|
||||||
/// Mutably borrow the last value in the array.
|
/// Mutably borrow the last value in the array.
|
||||||
pub fn last_mut(&mut self) -> StrResult<&mut Value> {
|
pub fn last_mut(&mut self) -> StrResult<&mut Value> {
|
||||||
Arc::make_mut(&mut self.0).last_mut().ok_or_else(array_is_empty)
|
self.0.make_mut().last_mut().ok_or_else(array_is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow the value at the given index.
|
/// Borrow the value at the given index.
|
||||||
@ -74,18 +76,18 @@ impl Array {
|
|||||||
pub fn at_mut(&mut self, index: i64) -> StrResult<&mut Value> {
|
pub fn at_mut(&mut self, index: i64) -> StrResult<&mut Value> {
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
self.locate(index)
|
self.locate(index)
|
||||||
.and_then(move |i| Arc::make_mut(&mut self.0).get_mut(i))
|
.and_then(move |i| self.0.make_mut().get_mut(i))
|
||||||
.ok_or_else(|| out_of_bounds(index, len))
|
.ok_or_else(|| out_of_bounds(index, len))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a value to the end of the array.
|
/// Push a value to the end of the array.
|
||||||
pub fn push(&mut self, value: Value) {
|
pub fn push(&mut self, value: Value) {
|
||||||
Arc::make_mut(&mut self.0).push(value);
|
self.0.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the last value in the array.
|
/// Remove the last value in the array.
|
||||||
pub fn pop(&mut self) -> StrResult<Value> {
|
pub fn pop(&mut self) -> StrResult<Value> {
|
||||||
Arc::make_mut(&mut self.0).pop().ok_or_else(array_is_empty)
|
self.0.pop().ok_or_else(array_is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a value at the specified index.
|
/// Insert a value at the specified index.
|
||||||
@ -96,7 +98,7 @@ impl Array {
|
|||||||
.filter(|&i| i <= self.0.len())
|
.filter(|&i| i <= self.0.len())
|
||||||
.ok_or_else(|| out_of_bounds(index, len))?;
|
.ok_or_else(|| out_of_bounds(index, len))?;
|
||||||
|
|
||||||
Arc::make_mut(&mut self.0).insert(i, value);
|
self.0.insert(i, value);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +110,7 @@ impl Array {
|
|||||||
.filter(|&i| i < self.0.len())
|
.filter(|&i| i < self.0.len())
|
||||||
.ok_or_else(|| out_of_bounds(index, len))?;
|
.ok_or_else(|| out_of_bounds(index, len))?;
|
||||||
|
|
||||||
Ok(Arc::make_mut(&mut self.0).remove(i))
|
Ok(self.0.remove(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a contigous subregion of the array.
|
/// Extract a contigous subregion of the array.
|
||||||
@ -126,7 +128,7 @@ impl Array {
|
|||||||
.ok_or_else(|| out_of_bounds(end, len))?
|
.ok_or_else(|| out_of_bounds(end, len))?
|
||||||
.max(start);
|
.max(start);
|
||||||
|
|
||||||
Ok(Self::from_vec(self.0[start..end].to_vec()))
|
Ok(Self::from_vec(self.0[start..end].into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the array contains a specific value.
|
/// Whether the array contains a specific value.
|
||||||
@ -170,7 +172,7 @@ impl Array {
|
|||||||
if func.argc().map_or(false, |count| count != 1) {
|
if func.argc().map_or(false, |count| count != 1) {
|
||||||
bail!(func.span(), "function must have exactly one parameter");
|
bail!(func.span(), "function must have exactly one parameter");
|
||||||
}
|
}
|
||||||
let mut kept = vec![];
|
let mut kept = EcoVec::new();
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
let args = Args::new(func.span(), [item.clone()]);
|
let args = Args::new(func.span(), [item.clone()]);
|
||||||
if func.call(vm, args)?.cast::<bool>().at(func.span())? {
|
if func.call(vm, args)?.cast::<bool>().at(func.span())? {
|
||||||
@ -244,7 +246,7 @@ impl Array {
|
|||||||
|
|
||||||
/// Return a new array with all items from this and nested arrays.
|
/// Return a new array with all items from this and nested arrays.
|
||||||
pub fn flatten(&self) -> Self {
|
pub fn flatten(&self) -> Self {
|
||||||
let mut flat = Vec::with_capacity(self.0.len());
|
let mut flat = EcoVec::with_capacity(self.0.len());
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
if let Value::Array(nested) = item {
|
if let Value::Array(nested) = item {
|
||||||
flat.extend(nested.flatten().into_iter());
|
flat.extend(nested.flatten().into_iter());
|
||||||
@ -287,8 +289,8 @@ impl Array {
|
|||||||
/// Returns an error if two values could not be compared.
|
/// Returns an error if two values could not be compared.
|
||||||
pub fn sorted(&self) -> StrResult<Self> {
|
pub fn sorted(&self) -> StrResult<Self> {
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
let mut vec = (*self.0).clone();
|
let mut vec = self.0.clone();
|
||||||
vec.sort_by(|a, b| {
|
vec.make_mut().sort_by(|a, b| {
|
||||||
a.partial_cmp(b).unwrap_or_else(|| {
|
a.partial_cmp(b).unwrap_or_else(|| {
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
result = Err(format_eco!(
|
result = Err(format_eco!(
|
||||||
@ -369,31 +371,28 @@ impl Add for Array {
|
|||||||
|
|
||||||
impl AddAssign for Array {
|
impl AddAssign for Array {
|
||||||
fn add_assign(&mut self, rhs: Array) {
|
fn add_assign(&mut self, rhs: Array) {
|
||||||
match Arc::try_unwrap(rhs.0) {
|
self.0.extend(rhs.0);
|
||||||
Ok(vec) => self.extend(vec),
|
|
||||||
Err(rc) => self.extend(rc.iter().cloned()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extend<Value> for Array {
|
impl Extend<Value> for Array {
|
||||||
fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) {
|
||||||
Arc::make_mut(&mut self.0).extend(iter);
|
self.0.extend(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<Value> for Array {
|
impl FromIterator<Value> for Array {
|
||||||
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
|
||||||
Self(Arc::new(iter.into_iter().collect()))
|
Self(iter.into_iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Array {
|
impl IntoIterator for Array {
|
||||||
type Item = Value;
|
type Item = Value;
|
||||||
type IntoIter = std::vec::IntoIter<Value>;
|
type IntoIter = ecow::IntoIter<Value>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Arc::take(self.0).into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ use std::num::NonZeroUsize;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
castable, Array, Content, Dict, Func, Label, Regex, Selector, Str, Transform, Value,
|
castable, Array, Content, Dict, Func, Label, Regex, Selector, Str, Transform, Value,
|
||||||
};
|
};
|
||||||
@ -13,7 +15,6 @@ use crate::geom::{
|
|||||||
Rel, Sides, Smart,
|
Rel, Sides, Smart,
|
||||||
};
|
};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// Cast from a value to a specific type.
|
/// Cast from a value to a specific type.
|
||||||
pub trait Cast<V = Value>: Sized {
|
pub trait Cast<V = Value>: Sized {
|
||||||
|
@ -6,6 +6,7 @@ use std::ops::{Add, AddAssign};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
|
use ecow::EcoString;
|
||||||
use siphasher::sip128::{Hasher128, SipHasher};
|
use siphasher::sip128::{Hasher128, SipHasher};
|
||||||
use thin_vec::ThinVec;
|
use thin_vec::ThinVec;
|
||||||
use typst_macros::node;
|
use typst_macros::node;
|
||||||
@ -16,7 +17,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::diag::{SourceResult, StrResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::{EcoString, ReadableTypeId};
|
use crate::util::ReadableTypeId;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Composable representation of styled content.
|
/// Composable representation of styled content.
|
||||||
|
@ -3,10 +3,12 @@ use std::fmt::{self, Debug, Formatter, Write};
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::{format_eco, EcoString};
|
||||||
|
|
||||||
use super::{array, Array, Str, Value};
|
use super::{array, Array, Str, Value};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::syntax::is_ident;
|
use crate::syntax::is_ident;
|
||||||
use crate::util::{format_eco, ArcExt, EcoString};
|
use crate::util::ArcExt;
|
||||||
|
|
||||||
/// Create a new [`Dict`] from key-value pairs.
|
/// Create a new [`Dict`] from key-value pairs.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -5,6 +5,7 @@ use std::mem;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use comemo::{Track, Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
|
use ecow::EcoVec;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -797,7 +798,7 @@ impl Eval for ast::Array {
|
|||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let items = self.items();
|
let items = self.items();
|
||||||
|
|
||||||
let mut vec = Vec::with_capacity(items.size_hint().0);
|
let mut vec = EcoVec::with_capacity(items.size_hint().0);
|
||||||
for item in items {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
||||||
|
@ -3,6 +3,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use comemo::{Track, Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Args, CastInfo, Dict, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector,
|
Args, CastInfo, Dict, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector,
|
||||||
@ -11,7 +12,6 @@ use super::{
|
|||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::syntax::ast::{self, AstNode, Expr};
|
use crate::syntax::ast::{self, AstNode, Expr};
|
||||||
use crate::syntax::{SourceId, Span, SyntaxNode};
|
use crate::syntax::{SourceId, Span, SyntaxNode};
|
||||||
use crate::util::EcoString;
|
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// An evaluatable function.
|
/// An evaluatable function.
|
||||||
|
@ -2,13 +2,14 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use super::{Content, Module, NodeId, StyleChain, StyleMap, Vt};
|
use super::{Content, Module, NodeId, StyleChain, StyleMap, Vt};
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::doc::Document;
|
use crate::doc::Document;
|
||||||
use crate::geom::{Abs, Dir};
|
use crate::geom::{Abs, Dir};
|
||||||
use crate::util::{hash128, EcoString};
|
use crate::util::hash128;
|
||||||
|
|
||||||
/// Definition of Typst's standard library.
|
/// Definition of Typst's standard library.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
//! Methods on values.
|
//! Methods on values.
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use super::{Args, Str, Value, Vm};
|
use super::{Args, Str, Value, Vm};
|
||||||
use crate::diag::{At, SourceResult};
|
use crate::diag::{At, SourceResult};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// Call a method on a value.
|
/// Call a method on a value.
|
||||||
pub fn call(
|
pub fn call(
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::{format_eco, EcoString};
|
||||||
|
|
||||||
use super::{Content, Scope, Value};
|
use super::{Content, Scope, Value};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
|
|
||||||
/// An evaluated module, ready for importing or typesetting.
|
/// An evaluated module, ready for importing or typesetting.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use ecow::format_eco;
|
||||||
|
|
||||||
use super::{format_str, Regex, Value};
|
use super::{format_str, Regex, Value};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Axes, Axis, GenAlign, Length, Numeric, PartialStroke, Rel, Smart};
|
use crate::geom::{Axes, Axis, GenAlign, Length, Numeric, PartialStroke, Rel, Smart};
|
||||||
use crate::util::format_eco;
|
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
||||||
/// Bail with a type mismatch error.
|
/// Bail with a type mismatch error.
|
||||||
|
@ -2,9 +2,10 @@ use std::collections::BTreeMap;
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use super::{Func, FuncType, Library, Value};
|
use super::{Func, FuncType, Library, Value};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// A stack of scopes.
|
/// A stack of scopes.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
@ -3,24 +3,26 @@ use std::fmt::{self, Debug, Display, Formatter, Write};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, AddAssign, Deref};
|
use std::ops::{Add, AddAssign, Deref};
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::{castable, dict, Array, Dict, Value};
|
use super::{castable, dict, Array, Dict, Value};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::GenAlign;
|
use crate::geom::GenAlign;
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
|
|
||||||
/// Create a new [`Str`] from a format string.
|
/// Create a new [`Str`] from a format string.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! __format_str {
|
macro_rules! __format_str {
|
||||||
($($tts:tt)*) => {{
|
($($tts:tt)*) => {{
|
||||||
$crate::model::Str::from(format_eco!($($tts)*))
|
$crate::model::Str::from($crate::model::format_eco!($($tts)*))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use crate::__format_str as format_str;
|
pub use crate::__format_str as format_str;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use ecow::format_eco;
|
||||||
|
|
||||||
/// An immutable reference counted string.
|
/// An immutable reference counted string.
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
@ -407,11 +409,13 @@ impl From<String> for Str {
|
|||||||
Self(s.into())
|
Self(s.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Cow<'_, str>> for Str {
|
impl From<Cow<'_, str>> for Str {
|
||||||
fn from(s: Cow<str>) -> Self {
|
fn from(s: Cow<str>) -> Self {
|
||||||
Self(s.into())
|
Self(s.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<char> for Str {
|
impl FromIterator<char> for Str {
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect())
|
Self(iter.into_iter().collect())
|
||||||
|
@ -3,8 +3,9 @@ use std::collections::BTreeSet;
|
|||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use typst_macros::symbols;
|
pub use typst_macros::symbols;
|
||||||
|
@ -4,6 +4,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::{format_eco, EcoString};
|
||||||
use siphasher::sip128::{Hasher128, SipHasher};
|
use siphasher::sip128::{Hasher128, SipHasher};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -13,7 +14,6 @@ use super::{
|
|||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
||||||
use crate::syntax::{ast, Span};
|
use crate::syntax::{ast, Span};
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
is_id_continue, is_id_start, is_newline, split_newlines, Span, SyntaxKind, SyntaxNode,
|
is_id_continue, is_id_start, is_newline, split_newlines, Span, SyntaxKind, SyntaxNode,
|
||||||
};
|
};
|
||||||
use crate::geom::{AbsUnit, AngleUnit};
|
use crate::geom::{AbsUnit, AngleUnit};
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// A typed AST node.
|
/// A typed AST node.
|
||||||
pub trait AstNode: Sized {
|
pub trait AstNode: Sized {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
use ecow::{format_eco, EcoString};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use super::{ErrorPos, SyntaxKind};
|
use super::{ErrorPos, SyntaxKind};
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
|
|
||||||
/// Splits up a string of source code into tokens.
|
/// Splits up a string of source code into tokens.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -3,10 +3,11 @@ use std::ops::{Deref, Range};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
use super::ast::AstNode;
|
use super::ast::AstNode;
|
||||||
use super::{SourceId, Span, SyntaxKind};
|
use super::{SourceId, Span, SyntaxKind};
|
||||||
use crate::diag::SourceError;
|
use crate::diag::SourceError;
|
||||||
use crate::util::EcoString;
|
|
||||||
|
|
||||||
/// A node in the untyped syntax tree.
|
/// A node in the untyped syntax tree.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use ecow::{format_eco, EcoString};
|
||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use super::{ast, is_newline, ErrorPos, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
use super::{ast, is_newline, ErrorPos, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
||||||
use crate::util::{format_eco, EcoString};
|
|
||||||
|
|
||||||
/// Parse a source file.
|
/// Parse a source file.
|
||||||
pub fn parse(text: &str) -> SyntaxNode {
|
pub fn parse(text: &str) -> SyntaxNode {
|
||||||
|
@ -6,7 +6,7 @@ use comemo::Prehashed;
|
|||||||
|
|
||||||
/// A shared buffer that is cheap to clone and hash.
|
/// A shared buffer that is cheap to clone and hash.
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct Buffer(Prehashed<Arc<Vec<u8>>>);
|
pub struct Buffer(Arc<Prehashed<Vec<u8>>>);
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
/// Return a view into the buffer.
|
/// Return a view into the buffer.
|
||||||
@ -22,19 +22,13 @@ impl Buffer {
|
|||||||
|
|
||||||
impl From<&[u8]> for Buffer {
|
impl From<&[u8]> for Buffer {
|
||||||
fn from(slice: &[u8]) -> Self {
|
fn from(slice: &[u8]) -> Self {
|
||||||
Self(Prehashed::new(Arc::new(slice.to_vec())))
|
Self(Arc::new(Prehashed::new(slice.to_vec())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for Buffer {
|
impl From<Vec<u8>> for Buffer {
|
||||||
fn from(vec: Vec<u8>) -> Self {
|
fn from(vec: Vec<u8>) -> Self {
|
||||||
Self(Prehashed::new(Arc::new(vec)))
|
Self(Arc::new(Prehashed::new(vec)))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Arc<Vec<u8>>> for Buffer {
|
|
||||||
fn from(arc: Arc<Vec<u8>>) -> Self {
|
|
||||||
Self(Prehashed::new(arc))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
515
src/util/eco.rs
515
src/util/eco.rs
@ -1,515 +0,0 @@
|
|||||||
use std::borrow::{Borrow, Cow};
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::ops::{Add, AddAssign, Deref};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::ArcExt;
|
|
||||||
|
|
||||||
/// Create a new [`EcoString`] from a format string.
|
|
||||||
#[macro_export]
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! __format_eco {
|
|
||||||
($($tts:tt)*) => {{
|
|
||||||
use std::fmt::Write;
|
|
||||||
let mut s = $crate::util::EcoString::new();
|
|
||||||
write!(s, $($tts)*).unwrap();
|
|
||||||
s
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use crate::__format_eco as format_eco;
|
|
||||||
|
|
||||||
/// An economical string with inline storage and clone-on-write semantics.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EcoString(Repr);
|
|
||||||
|
|
||||||
/// The internal representation. Either:
|
|
||||||
/// - inline when below a certain number of bytes, or
|
|
||||||
/// - reference-counted on the heap with clone-on-write semantics.
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum Repr {
|
|
||||||
Small { buf: [u8; LIMIT], len: u8 },
|
|
||||||
Large(Arc<String>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The maximum number of bytes that can be stored inline.
|
|
||||||
///
|
|
||||||
/// The value is chosen such that an `EcoString` fits exactly into 16 bytes
|
|
||||||
/// (which are needed anyway due to the `Arc`s alignment, at least on 64-bit
|
|
||||||
/// platforms).
|
|
||||||
///
|
|
||||||
/// Must be at least 4 to hold any char.
|
|
||||||
const LIMIT: usize = 14;
|
|
||||||
|
|
||||||
impl EcoString {
|
|
||||||
/// Create a new, empty string.
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self(Repr::Small { buf: [0; LIMIT], len: 0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new, empty string with the given `capacity`.
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
if capacity <= LIMIT {
|
|
||||||
Self::new()
|
|
||||||
} else {
|
|
||||||
Self(Repr::Large(Arc::new(String::with_capacity(capacity))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an instance from an existing string-like type.
|
|
||||||
pub fn from_str_like<S>(s: S) -> Self
|
|
||||||
where
|
|
||||||
S: AsRef<str> + Into<String>,
|
|
||||||
{
|
|
||||||
let slice = s.as_ref();
|
|
||||||
let len = slice.len();
|
|
||||||
Self(if len <= LIMIT {
|
|
||||||
let mut buf = [0; LIMIT];
|
|
||||||
buf[..len].copy_from_slice(slice.as_bytes());
|
|
||||||
Repr::Small { buf, len: len as u8 }
|
|
||||||
} else {
|
|
||||||
Repr::Large(Arc::new(s.into()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the string is empty.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The length of the string in bytes.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match &self.0 {
|
|
||||||
Repr::Small { len, .. } => usize::from(*len),
|
|
||||||
Repr::Large(string) => string.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A string slice containing the entire string.
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append the given character at the end.
|
|
||||||
pub fn push(&mut self, c: char) {
|
|
||||||
match &mut self.0 {
|
|
||||||
Repr::Small { buf, len } => {
|
|
||||||
let prev = usize::from(*len);
|
|
||||||
if c.len_utf8() == 1 && prev < LIMIT {
|
|
||||||
buf[prev] = c as u8;
|
|
||||||
*len += 1;
|
|
||||||
} else {
|
|
||||||
self.push_str(c.encode_utf8(&mut [0; 4]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Repr::Large(rc) => Arc::make_mut(rc).push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append the given string slice at the end.
|
|
||||||
pub fn push_str(&mut self, string: &str) {
|
|
||||||
match &mut self.0 {
|
|
||||||
Repr::Small { buf, len } => {
|
|
||||||
let prev = usize::from(*len);
|
|
||||||
let new = prev + string.len();
|
|
||||||
if new <= LIMIT {
|
|
||||||
buf[prev..new].copy_from_slice(string.as_bytes());
|
|
||||||
*len = new as u8;
|
|
||||||
} else {
|
|
||||||
let mut spilled = String::with_capacity(new);
|
|
||||||
spilled.push_str(self);
|
|
||||||
spilled.push_str(string);
|
|
||||||
self.0 = Repr::Large(Arc::new(spilled));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Repr::Large(rc) => Arc::make_mut(rc).push_str(string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the last character from the string.
|
|
||||||
pub fn pop(&mut self) -> Option<char> {
|
|
||||||
let c = self.as_str().chars().rev().next()?;
|
|
||||||
match &mut self.0 {
|
|
||||||
Repr::Small { len, .. } => {
|
|
||||||
*len -= c.len_utf8() as u8;
|
|
||||||
}
|
|
||||||
Repr::Large(rc) => {
|
|
||||||
Arc::make_mut(rc).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear the string.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
match &mut self.0 {
|
|
||||||
Repr::Small { len, .. } => *len = 0,
|
|
||||||
Repr::Large(rc) => {
|
|
||||||
if Arc::strong_count(rc) == 1 {
|
|
||||||
Arc::make_mut(rc).clear();
|
|
||||||
} else {
|
|
||||||
*self = Self::new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the string to lowercase.
|
|
||||||
pub fn to_lowercase(&self) -> Self {
|
|
||||||
if let Repr::Small { mut buf, len } = self.0 {
|
|
||||||
if self.is_ascii() {
|
|
||||||
buf[..usize::from(len)].make_ascii_lowercase();
|
|
||||||
return Self(Repr::Small { buf, len });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.as_str().to_lowercase().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the string to uppercase.
|
|
||||||
pub fn to_uppercase(&self) -> Self {
|
|
||||||
if let Repr::Small { mut buf, len } = self.0 {
|
|
||||||
if self.is_ascii() {
|
|
||||||
buf[..usize::from(len)].make_ascii_uppercase();
|
|
||||||
return Self(Repr::Small { buf, len });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.as_str().to_uppercase().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Repeat this string `n` times.
|
|
||||||
pub fn repeat(&self, n: usize) -> Self {
|
|
||||||
if n == 0 {
|
|
||||||
return Self::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Repr::Small { buf, len } = self.0 {
|
|
||||||
let prev = usize::from(len);
|
|
||||||
let new = prev.saturating_mul(n);
|
|
||||||
if new <= LIMIT {
|
|
||||||
let src = &buf[..prev];
|
|
||||||
let mut buf = [0; LIMIT];
|
|
||||||
for i in 0..n {
|
|
||||||
buf[prev * i..prev * (i + 1)].copy_from_slice(src);
|
|
||||||
}
|
|
||||||
return Self(Repr::Small { buf, len: new as u8 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.as_str().repeat(n).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for EcoString {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
fn deref(&self) -> &str {
|
|
||||||
match &self.0 {
|
|
||||||
// Safety:
|
|
||||||
// The buffer contents stem from correct UTF-8 sources:
|
|
||||||
// - Valid ASCII characters
|
|
||||||
// - Other string slices
|
|
||||||
// - Chars that were encoded with char::encode_utf8
|
|
||||||
// Furthermore, we still do the bounds-check on the len in case
|
|
||||||
// it gets corrupted somehow.
|
|
||||||
Repr::Small { buf, len } => unsafe {
|
|
||||||
std::str::from_utf8_unchecked(&buf[..usize::from(*len)])
|
|
||||||
},
|
|
||||||
Repr::Large(string) => string.as_str(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EcoString {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for EcoString {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
Debug::fmt(self.as_str(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for EcoString {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
Display::fmt(self.as_str(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for EcoString {}
|
|
||||||
|
|
||||||
impl PartialEq for EcoString {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.as_str().eq(other.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<str> for EcoString {
|
|
||||||
fn eq(&self, other: &str) -> bool {
|
|
||||||
self.as_str().eq(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<&str> for EcoString {
|
|
||||||
fn eq(&self, other: &&str) -> bool {
|
|
||||||
self.as_str().eq(*other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<EcoString> for str {
|
|
||||||
fn eq(&self, other: &EcoString) -> bool {
|
|
||||||
self.eq(other.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<EcoString> for &str {
|
|
||||||
fn eq(&self, other: &EcoString) -> bool {
|
|
||||||
(*self).eq(other.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for EcoString {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.as_str().cmp(other.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for EcoString {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
self.as_str().partial_cmp(other.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for EcoString {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.as_str().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for EcoString {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
self.push_str(s);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
|
||||||
self.push(c);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for EcoString {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(mut self, rhs: Self) -> Self::Output {
|
|
||||||
self += rhs;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for EcoString {
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
self.push_str(rhs.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for EcoString {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Borrow<str> for EcoString {
|
|
||||||
fn borrow(&self) -> &str {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<char> for EcoString {
|
|
||||||
fn from(c: char) -> Self {
|
|
||||||
let mut buf = [0; LIMIT];
|
|
||||||
let len = c.encode_utf8(&mut buf).len();
|
|
||||||
Self(Repr::Small { buf, len: len as u8 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for EcoString {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Self::from_str_like(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for EcoString {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self::from_str_like(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Cow<'_, str>> for EcoString {
|
|
||||||
fn from(s: Cow<str>) -> Self {
|
|
||||||
match s {
|
|
||||||
Cow::Borrowed(s) => s.into(),
|
|
||||||
Cow::Owned(s) => s.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<char> for EcoString {
|
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
|
||||||
let mut s = Self::new();
|
|
||||||
for c in iter {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<Self> for EcoString {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
|
|
||||||
let mut s = Self::new();
|
|
||||||
for piece in iter {
|
|
||||||
s.push_str(&piece);
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Extend<char> for EcoString {
|
|
||||||
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
|
|
||||||
for c in iter {
|
|
||||||
self.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EcoString> for String {
|
|
||||||
fn from(s: EcoString) -> Self {
|
|
||||||
match s.0 {
|
|
||||||
Repr::Small { .. } => s.as_str().to_owned(),
|
|
||||||
Repr::Large(rc) => Arc::take(rc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&EcoString> for String {
|
|
||||||
fn from(s: &EcoString) -> Self {
|
|
||||||
s.as_str().to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const ALPH: &str = "abcdefghijklmnopqrstuvwxyz";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_new() {
|
|
||||||
// Test inline strings.
|
|
||||||
assert_eq!(EcoString::new(), "");
|
|
||||||
assert_eq!(EcoString::from('a'), "a");
|
|
||||||
assert_eq!(EcoString::from('😀'), "😀");
|
|
||||||
assert_eq!(EcoString::from("abc"), "abc");
|
|
||||||
|
|
||||||
// Test around the inline limit.
|
|
||||||
assert_eq!(EcoString::from(&ALPH[..LIMIT - 1]), ALPH[..LIMIT - 1]);
|
|
||||||
assert_eq!(EcoString::from(&ALPH[..LIMIT]), ALPH[..LIMIT]);
|
|
||||||
assert_eq!(EcoString::from(&ALPH[..LIMIT + 1]), ALPH[..LIMIT + 1]);
|
|
||||||
|
|
||||||
// Test heap string.
|
|
||||||
assert_eq!(EcoString::from(ALPH), ALPH);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_push() {
|
|
||||||
let mut v = EcoString::new();
|
|
||||||
v.push('a');
|
|
||||||
v.push('b');
|
|
||||||
v.push_str("cd😀");
|
|
||||||
assert_eq!(v, "abcd😀");
|
|
||||||
assert_eq!(v.len(), 8);
|
|
||||||
|
|
||||||
// Test fully filling the inline storage.
|
|
||||||
v.push_str("efghij");
|
|
||||||
assert_eq!(v.len(), LIMIT);
|
|
||||||
|
|
||||||
// Test spilling with `push`.
|
|
||||||
let mut a = v.clone();
|
|
||||||
a.push('k');
|
|
||||||
assert_eq!(a, "abcd😀efghijk");
|
|
||||||
assert_eq!(a.len(), 15);
|
|
||||||
|
|
||||||
// Test spilling with `push_str`.
|
|
||||||
let mut b = v.clone();
|
|
||||||
b.push_str("klmn");
|
|
||||||
assert_eq!(b, "abcd😀efghijklmn");
|
|
||||||
assert_eq!(b.len(), 18);
|
|
||||||
|
|
||||||
// v should be unchanged.
|
|
||||||
assert_eq!(v.len(), LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_pop() {
|
|
||||||
// Test with inline string.
|
|
||||||
let mut v = EcoString::from("Hello World!");
|
|
||||||
assert_eq!(v.pop(), Some('!'));
|
|
||||||
assert_eq!(v, "Hello World");
|
|
||||||
|
|
||||||
// Remove one-by-one.
|
|
||||||
for _ in 0..10 {
|
|
||||||
v.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(v, "H");
|
|
||||||
assert_eq!(v.pop(), Some('H'));
|
|
||||||
assert_eq!(v, "");
|
|
||||||
assert!(v.is_empty());
|
|
||||||
|
|
||||||
// Test with large string.
|
|
||||||
let mut v = EcoString::from(ALPH);
|
|
||||||
assert_eq!(v.pop(), Some('z'));
|
|
||||||
assert_eq!(v.len(), 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_index() {
|
|
||||||
// Test that we can use the index syntax.
|
|
||||||
let v = EcoString::from("abc");
|
|
||||||
assert_eq!(&v[..2], "ab");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_case() {
|
|
||||||
assert_eq!(EcoString::new().to_uppercase(), "");
|
|
||||||
assert_eq!(EcoString::from("abc").to_uppercase(), "ABC");
|
|
||||||
assert_eq!(EcoString::from("AΣ").to_lowercase(), "aς");
|
|
||||||
assert_eq!(
|
|
||||||
EcoString::from("a").repeat(100).to_uppercase(),
|
|
||||||
EcoString::from("A").repeat(100)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
EcoString::from("Ö").repeat(20).to_lowercase(),
|
|
||||||
EcoString::from("ö").repeat(20)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_repeat() {
|
|
||||||
// Test with empty string.
|
|
||||||
assert_eq!(EcoString::new().repeat(0), "");
|
|
||||||
assert_eq!(EcoString::new().repeat(100), "");
|
|
||||||
|
|
||||||
// Test non-spilling and spilling case.
|
|
||||||
let v = EcoString::from("abc");
|
|
||||||
assert_eq!(v.repeat(0), "");
|
|
||||||
assert_eq!(v.repeat(3), "abcabcabc");
|
|
||||||
assert_eq!(v.repeat(5), "abcabcabcabcabc");
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,12 +2,9 @@
|
|||||||
|
|
||||||
pub mod fat;
|
pub mod fat;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod eco;
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
|
||||||
pub use buffer::Buffer;
|
pub use buffer::Buffer;
|
||||||
pub use eco::{format_eco, EcoString};
|
|
||||||
|
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user