Skip to content

Commit 671e93b

Browse files
committed
perf: Avoid using RefCell for nth index caches
1 parent fbf7ed2 commit 671e93b

3 files changed

Lines changed: 25 additions & 17 deletions

File tree

css-inline/src/html/document.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::{
99
use crate::{html::DocumentStyleMap, InlineError};
1010
use html5ever::local_name;
1111
use selectors::NthIndexCache;
12-
use std::{cell::RefCell, fmt, fmt::Formatter, io::Write, iter::successors};
12+
use std::{fmt, fmt::Formatter, io::Write, iter::successors};
1313

1414
/// HTML document representation.
1515
///
@@ -39,7 +39,7 @@ use std::{cell::RefCell, fmt, fmt::Formatter, io::Write, iter::successors};
3939
pub(crate) struct Document {
4040
pub(crate) nodes: Vec<Node>,
4141
/// Ids of Element nodes & caches for their nth index selectors.
42-
pub(crate) elements: Vec<(NodeId, RefCell<NthIndexCache>)>,
42+
pub(crate) elements: Vec<NodeId>,
4343
/// Ids of `style` nodes.
4444
styles: Vec<NodeId>,
4545
/// Ids of `link` nodes, specifically those with the `rel` attribute value set as `stylesheet`.
@@ -122,7 +122,7 @@ impl Document {
122122

123123
#[inline]
124124
pub(super) fn push_element_id(&mut self, node: NodeId) {
125-
self.elements.push((node, RefCell::default()));
125+
self.elements.push(node);
126126
}
127127

128128
/// Detach a node from its siblings and its parent.
@@ -279,11 +279,18 @@ impl Document {
279279
}
280280

281281
/// Filter this node iterator to elements matching the given selectors.
282-
pub(crate) fn select<'a, 'b>(
282+
pub(crate) fn select<'a, 'b, 'c>(
283283
&'a self,
284284
selectors: &'b str,
285-
) -> Result<Select<'a>, ParseError<'b>> {
286-
select(self, selectors)
285+
caches: &'c mut [NthIndexCache],
286+
) -> Result<Select<'a, 'c>, ParseError<'b>> {
287+
select(self, selectors, caches)
288+
}
289+
290+
pub(crate) fn build_caches(&self) -> Vec<NthIndexCache> {
291+
(0..self.elements.len())
292+
.map(|_| NthIndexCache::default())
293+
.collect()
287294
}
288295
}
289296

css-inline/src/html/iter.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,39 @@ use super::{
66
Specificity,
77
};
88
use selectors::NthIndexCache;
9-
use std::cell::RefCell;
9+
use std::iter::Zip;
1010

1111
/// Compile selectors from a string and create an element iterator that yields elements matching these selectors.
1212
#[inline]
13-
pub(crate) fn select<'a, 'b>(
13+
pub(crate) fn select<'a, 'b, 'c>(
1414
document: &'a Document,
1515
selectors: &'b str,
16-
) -> Result<Select<'a>, ParseError<'b>> {
16+
caches: &'c mut [NthIndexCache],
17+
) -> Result<Select<'a, 'c>, ParseError<'b>> {
1718
Selectors::compile(selectors).map(|selectors| Select {
1819
document,
19-
iter: document.elements.iter(),
20+
iter: document.elements.iter().zip(caches.iter_mut()),
2021
selectors,
2122
})
2223
}
2324

2425
/// An element iterator adaptor that yields elements matching given selectors.
25-
pub(crate) struct Select<'a> {
26+
pub(crate) struct Select<'a, 'c> {
2627
document: &'a Document,
27-
iter: std::slice::Iter<'a, (NodeId, RefCell<NthIndexCache>)>,
28+
iter: Zip<std::slice::Iter<'a, NodeId>, std::slice::IterMut<'c, NthIndexCache>>,
2829
/// The selectors to be matched.
2930
selectors: Selectors,
3031
}
3132

32-
impl<'a> Select<'a> {
33+
impl<'a, 'c> Select<'a, 'c> {
3334
/// Specificity of the first selector in the list of selectors.
3435
#[inline]
3536
pub(crate) fn specificity(&self) -> Specificity {
3637
Specificity::new(self.selectors.0[0].specificity())
3738
}
3839
}
3940

40-
impl<'a> Iterator for Select<'a> {
41+
impl<'a, 'c> Iterator for Select<'a, 'c> {
4142
type Item = Element<'a>;
4243

4344
#[inline]
@@ -47,9 +48,8 @@ impl<'a> Iterator for Select<'a> {
4748
unreachable!("Element ids always point to element nodes")
4849
};
4950
let element = Element::new(self.document, *element_id, element);
50-
let mut cache = cache.borrow_mut();
5151
for selector in self.selectors.iter() {
52-
if element.matches(selector, &mut cache) {
52+
if element.matches(selector, cache) {
5353
return Some(element);
5454
}
5555
}

css-inline/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,12 @@ impl<'a> CSSInliner<'a> {
295295
{
296296
rule_list.push(rule);
297297
}
298+
let mut caches = document.build_caches();
298299
for (selectors, (start, end)) in &rule_list {
299300
// Only CSS Syntax Level 3 is supported, therefore it is OK to split by `,`
300301
// With `is` or `where` selectors (Level 4) this split should be done on the parser level
301302
for selector in selectors.split(',') {
302-
if let Ok(matching_elements) = document.select(selector) {
303+
if let Ok(matching_elements) = document.select(selector, &mut caches) {
303304
let specificity = matching_elements.specificity();
304305
for matching_element in matching_elements {
305306
let element_styles =

0 commit comments

Comments
 (0)