Skip to content

Commit 89092df

Browse files
committed
perf: Avoid some String allocations during parsing
1 parent cb11184 commit 89092df

File tree

2 files changed

+29
-24
lines changed

2 files changed

+29
-24
lines changed

src/lib.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,16 @@ pub use error::InlineError;
136136
use std::collections::HashMap;
137137

138138
#[derive(Debug)]
139-
struct Rule {
139+
struct Rule<'i> {
140140
selectors: Selectors,
141-
declarations: Vec<parser::Declaration>,
141+
declarations: Vec<parser::Declaration<'i>>,
142142
}
143143

144-
impl Rule {
145-
pub fn new(selectors: &str, declarations: Vec<parser::Declaration>) -> Result<Rule, ()> {
144+
impl<'i> Rule<'i> {
145+
pub fn new(
146+
selectors: &str,
147+
declarations: Vec<parser::Declaration<'i>>,
148+
) -> Result<Rule<'i>, ()> {
146149
Ok(Rule {
147150
selectors: Selectors::compile(selectors)?,
148151
declarations,
@@ -213,7 +216,7 @@ impl CSSInliner {
213216
let mut parser = parser::CSSParser::new(&mut parse_input);
214217
for parsed in parser.parse() {
215218
if let Ok((selector, declarations)) = parsed {
216-
let rule = Rule::new(&selector, declarations).map_err(|_| {
219+
let rule = Rule::new(selector, declarations).map_err(|_| {
217220
error::InlineError::ParseError("Unknown error".to_string())
218221
})?;
219222
let matching_elements = document
@@ -227,7 +230,7 @@ impl CSSInliner {
227230
} else {
228231
rule.declarations
229232
.iter()
230-
.map(|&(ref key, ref value)| format!("{}:{};", key, value))
233+
.map(|&(ref key, value)| format!("{}:{};", key, value))
231234
.collect()
232235
};
233236
attributes.insert("style", style);
@@ -272,18 +275,18 @@ fn merge_styles(
272275
cssparser::DeclarationListParser::new(&mut parser, parser::CSSDeclarationListParser);
273276
// Merge existing with the new ones
274277
// We know that at least one rule already exists, so we add 1
275-
let mut styles: HashMap<String, String> =
278+
let mut styles: HashMap<String, &str> =
276279
HashMap::with_capacity(new_styles.len().saturating_add(1));
277-
for declaration in declarations.into_iter() {
280+
for declaration in declarations {
278281
let (property, value) = declaration?;
279-
styles.insert(property, value);
282+
styles.insert(property.to_string(), value);
280283
}
281-
for (property, value) in new_styles.iter() {
282-
styles.insert(property.to_string(), value.to_string());
284+
for (property, value) in new_styles {
285+
styles.insert(property.to_string(), value);
283286
}
284287
// Create a new declarations list
285288
Ok(styles
286-
.iter()
289+
.into_iter()
287290
.map(|(key, value)| format!("{}:{};", key, value))
288291
.collect::<String>())
289292
}

src/parser.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
pub struct CSSRuleListParser;
22
pub(crate) struct CSSDeclarationListParser;
33

4-
pub type Declaration = (String, String);
5-
pub type QualifiedRule = (String, Vec<Declaration>);
4+
pub type Declaration<'i> = (cssparser::CowRcStr<'i>, &'i str);
5+
pub type QualifiedRule<'i> = (&'i str, Vec<Declaration<'i>>);
66

7-
fn exhaust(input: &mut cssparser::Parser) -> String {
7+
fn exhaust<'i>(input: &mut cssparser::Parser<'i, '_>) -> &'i str {
88
let start = input.position();
99
while input.next().is_ok() {}
10-
input.slice_from(start).to_string()
10+
input.slice_from(start)
1111
}
1212

1313
/// Parser for qualified rules - a prelude + a simple {} block.
1414
///
1515
/// Usually these rules are a selector + list of declarations: `p { color: blue; font-size: 2px }`
1616
impl<'i> cssparser::QualifiedRuleParser<'i> for CSSRuleListParser {
17-
type Prelude = String;
18-
type QualifiedRule = QualifiedRule;
17+
type Prelude = &'i str;
18+
type QualifiedRule = QualifiedRule<'i>;
1919
type Error = ();
2020

2121
fn parse_prelude<'t>(
@@ -48,31 +48,31 @@ impl<'i> cssparser::QualifiedRuleParser<'i> for CSSRuleListParser {
4848

4949
/// Parse a declaration within {} block: `color: blue`
5050
impl<'i> cssparser::DeclarationParser<'i> for CSSDeclarationListParser {
51-
type Declaration = Declaration;
51+
type Declaration = Declaration<'i>;
5252
type Error = ();
5353

5454
fn parse_value<'t>(
5555
&mut self,
5656
name: cssparser::CowRcStr<'i>,
5757
input: &mut cssparser::Parser<'i, 't>,
5858
) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
59-
Ok((name.to_string(), exhaust(input)))
59+
Ok((name, exhaust(input)))
6060
}
6161
}
6262

63-
impl cssparser::AtRuleParser<'_> for CSSRuleListParser {
63+
impl<'i> cssparser::AtRuleParser<'i> for CSSRuleListParser {
6464
type PreludeNoBlock = String;
6565
type PreludeBlock = String;
66-
type AtRule = QualifiedRule;
66+
type AtRule = QualifiedRule<'i>;
6767
type Error = ();
6868
}
6969

7070
/// Parsing for at-rules, e.g: `@charset "utf-8";`
7171
/// Since they are can not be inlined we use the default implementation, that rejects all at-rules.
72-
impl cssparser::AtRuleParser<'_> for CSSDeclarationListParser {
72+
impl<'i> cssparser::AtRuleParser<'i> for CSSDeclarationListParser {
7373
type PreludeNoBlock = String;
7474
type PreludeBlock = String;
75-
type AtRule = Declaration;
75+
type AtRule = Declaration<'i>;
7676
type Error = ();
7777
}
7878

@@ -81,12 +81,14 @@ pub struct CSSParser<'i, 't> {
8181
}
8282

8383
impl<'i: 't, 't> CSSParser<'i, 't> {
84+
#[inline]
8485
pub fn new(css: &'t mut cssparser::ParserInput<'i>) -> CSSParser<'i, 't> {
8586
CSSParser {
8687
input: cssparser::Parser::new(css),
8788
}
8889
}
8990

91+
#[inline]
9092
pub fn parse<'a>(&'a mut self) -> cssparser::RuleListParser<'i, 't, 'a, CSSRuleListParser> {
9193
cssparser::RuleListParser::new_for_stylesheet(&mut self.input, CSSRuleListParser)
9294
}

0 commit comments

Comments
 (0)