implement string interning
This commit is contained in:
parent
5551a8f8a8
commit
2dfda192bc
@ -64,6 +64,7 @@ beef = "0.4.4"
|
||||
# long to compile, and you cannot make dev-dependencies optional
|
||||
criterion = { version = "0.3.2", optional = true }
|
||||
indexmap = "1.4.0"
|
||||
lasso = "0.2.2"
|
||||
|
||||
[features]
|
||||
default = ["commandline", "random"]
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::fmt::{self, Display, Write};
|
||||
|
||||
use crate::interner::InternedString;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Op {
|
||||
Equal,
|
||||
@ -111,27 +113,35 @@ impl ListSeparator {
|
||||
/// This struct protects that invariant by normalizing all
|
||||
/// underscores into hypens.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub(crate) struct Identifier(String);
|
||||
pub(crate) struct Identifier(InternedString);
|
||||
|
||||
impl From<String> for Identifier {
|
||||
fn from(s: String) -> Identifier {
|
||||
if s.contains('_') {
|
||||
Identifier(s.replace('_', "-"))
|
||||
Identifier(InternedString::get_or_intern(if s.contains('_') {
|
||||
s.replace('_', "-")
|
||||
} else {
|
||||
Identifier(s)
|
||||
}
|
||||
s
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for Identifier {
|
||||
fn from(s: &String) -> Identifier {
|
||||
Identifier(s.replace('_', "-"))
|
||||
if s.contains('_') {
|
||||
Identifier(InternedString::get_or_intern(s.replace('_', "-")))
|
||||
} else {
|
||||
Identifier(InternedString::get_or_intern(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Identifier {
|
||||
fn from(s: &str) -> Identifier {
|
||||
Identifier(s.replace('_', "-"))
|
||||
if s.contains('_') {
|
||||
Identifier(InternedString::get_or_intern(s.replace('_', "-")))
|
||||
} else {
|
||||
Identifier(InternedString::get_or_intern(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,15 +151,9 @@ impl Display for Identifier {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Identifier {
|
||||
fn default() -> Self {
|
||||
Self(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
self.0.resolve_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
37
src/interner.rs
Normal file
37
src/interner.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use lasso::{Rodeo, Spur};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
thread_local!(static STRINGS: RefCell<Rodeo<Spur>> = RefCell::new(Rodeo::default()));
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub(crate) struct InternedString(Spur);
|
||||
|
||||
impl InternedString {
|
||||
pub fn get_or_intern<T: AsRef<str>>(s: T) -> Self {
|
||||
Self(STRINGS.with(|interner| interner.borrow_mut().get_or_intern(s)))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn resolve(self) -> String {
|
||||
STRINGS.with(|interner| interner.borrow().resolve(&self.0).to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.resolve_ref() == ""
|
||||
}
|
||||
|
||||
pub fn resolve_ref<'a>(self) -> &'a str {
|
||||
unsafe {
|
||||
STRINGS.with(|interner| &(*(interner.as_ptr()).as_ref().unwrap().resolve(&self.0)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InternedString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
STRINGS.with(|interner| write!(f, "{}", interner.borrow().resolve(&self.0)))
|
||||
}
|
||||
}
|
@ -116,6 +116,7 @@ mod selector;
|
||||
mod style;
|
||||
mod token;
|
||||
mod unit;
|
||||
mod interner;
|
||||
mod utils;
|
||||
mod value;
|
||||
|
||||
|
@ -351,18 +351,14 @@ impl<'a> Parser<'a> {
|
||||
Ok(vals)
|
||||
}
|
||||
|
||||
pub(super) fn eval_args(
|
||||
&mut self,
|
||||
mut fn_args: FuncArgs,
|
||||
mut args: CallArgs,
|
||||
) -> SassResult<Scope> {
|
||||
pub(super) fn eval_args(&mut self, fn_args: FuncArgs, mut args: CallArgs) -> SassResult<Scope> {
|
||||
let mut scope = Scope::new();
|
||||
if fn_args.0.is_empty() {
|
||||
args.max_args(0)?;
|
||||
return Ok(scope);
|
||||
}
|
||||
self.scopes.enter_new_scope();
|
||||
for (idx, arg) in fn_args.0.iter_mut().enumerate() {
|
||||
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
|
||||
if arg.is_variadic {
|
||||
let span = args.span();
|
||||
let arg_list = Value::ArgList(self.variadic_args(args)?);
|
||||
@ -387,7 +383,7 @@ impl<'a> Parser<'a> {
|
||||
},
|
||||
}?;
|
||||
self.scopes.insert_var(arg.name.clone(), val.clone());
|
||||
scope.insert_var(mem::take(&mut arg.name), val);
|
||||
scope.insert_var(arg.name, val);
|
||||
}
|
||||
self.scopes.exit_scope();
|
||||
Ok(scope)
|
||||
|
@ -2,7 +2,7 @@ use std::ops::{BitAnd, BitOr};
|
||||
|
||||
use codemap::Spanned;
|
||||
|
||||
use crate::value::Value;
|
||||
use crate::{interner::InternedString, value::Value};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct NeverEmptyVec<T> {
|
||||
@ -41,7 +41,7 @@ impl<T> NeverEmptyVec<T> {
|
||||
#[derive(Debug)]
|
||||
pub(super) enum SelectorOrStyle {
|
||||
Selector(String),
|
||||
Style(String, Option<Box<Spanned<Value>>>),
|
||||
Style(InternedString, Option<Box<Spanned<Value>>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
@ -2,6 +2,7 @@ use codemap::Spanned;
|
||||
|
||||
use crate::{
|
||||
error::SassResult,
|
||||
interner::InternedString,
|
||||
style::Style,
|
||||
utils::{is_name, is_name_start},
|
||||
value::Value,
|
||||
@ -126,7 +127,10 @@ impl<'a> Parser<'a> {
|
||||
let len = toks.len();
|
||||
if let Ok(val) = self.parse_value_from_vec(toks) {
|
||||
self.toks.take(len).for_each(drop);
|
||||
return Ok(SelectorOrStyle::Style(property, Some(Box::new(val))));
|
||||
return Ok(SelectorOrStyle::Style(
|
||||
InternedString::get_or_intern(property),
|
||||
Some(Box::new(val)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +140,7 @@ impl<'a> Parser<'a> {
|
||||
property.push(':');
|
||||
return Ok(SelectorOrStyle::Selector(property));
|
||||
}
|
||||
_ => SelectorOrStyle::Style(property, None),
|
||||
_ => SelectorOrStyle::Style(InternedString::get_or_intern(property), None),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -172,7 +176,10 @@ impl<'a> Parser<'a> {
|
||||
self.parse_value()
|
||||
}
|
||||
|
||||
pub(super) fn parse_style_group(&mut self, super_property: String) -> SassResult<Vec<Style>> {
|
||||
pub(super) fn parse_style_group(
|
||||
&mut self,
|
||||
super_property: InternedString,
|
||||
) -> SassResult<Vec<Style>> {
|
||||
let mut styles = Vec::new();
|
||||
self.whitespace();
|
||||
while let Some(tok) = self.toks.peek().cloned() {
|
||||
@ -181,7 +188,9 @@ impl<'a> Parser<'a> {
|
||||
self.toks.next();
|
||||
self.whitespace();
|
||||
loop {
|
||||
let property = self.parse_property(super_property.clone())?;
|
||||
let property = InternedString::get_or_intern(
|
||||
self.parse_property(super_property.resolve())?,
|
||||
);
|
||||
if let Some(tok) = self.toks.peek() {
|
||||
if tok.kind == '{' {
|
||||
styles.append(&mut self.parse_style_group(property)?);
|
||||
|
@ -1,11 +1,11 @@
|
||||
use codemap::Spanned;
|
||||
|
||||
use crate::{error::SassResult, value::Value};
|
||||
use crate::{error::SassResult, interner::InternedString, value::Value};
|
||||
|
||||
/// A style: `color: red`
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Style {
|
||||
pub property: String,
|
||||
pub property: InternedString,
|
||||
pub value: Box<Spanned<Value>>,
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user