Skip to content

Commit 1530bee

Browse files
committed
perf: Reuse caches for nth index selectors
1 parent 9024970 commit 1530bee

File tree

7 files changed

+31
-14
lines changed

7 files changed

+31
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Performance
66

77
- Avoid iterating over non-Element nodes.
8+
- Reuse caches for nth index selectors.
89

910
## [0.11.1] - 2023-12-04
1011

bindings/python/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Performance
66

77
- Avoid iterating over non-Element nodes.
8+
- Reuse caches for nth index selectors.
89

910
## [0.11.1] - 2023-12-09
1011

bindings/ruby/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Performance
66

77
- Avoid iterating over non-Element nodes.
8+
- Reuse caches for nth index selectors.
89

910
## [0.11.1] - 2023-12-09
1011

bindings/wasm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Performance
66

77
- Avoid iterating over non-Element nodes.
8+
- Reuse caches for nth index selectors.
89

910
## [0.11.1] - 2023-12-09
1011

css-inline/src/html/document.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use super::{
88
};
99
use crate::{html::DocumentStyleMap, InlineError};
1010
use html5ever::local_name;
11-
use std::{io::Write, iter::successors};
11+
use selectors::NthIndexCache;
12+
use std::{cell::RefCell, fmt, fmt::Formatter, io::Write, iter::successors};
1213

1314
/// HTML document representation.
1415
///
@@ -35,18 +36,28 @@ use std::{io::Write, iter::successors};
3536
///
3637
/// Each Node within the `Document` is interconnected with its siblings, and has a parent-child
3738
/// relationship with its descendants.
38-
#[derive(Debug)]
3939
pub(crate) struct Document {
4040
pub(crate) nodes: Vec<Node>,
41-
/// Ids of Element nodes.
42-
pub(crate) elements: Vec<NodeId>,
41+
/// Ids of Element nodes & caches for their nth index selectors.
42+
pub(crate) elements: Vec<(NodeId, RefCell<NthIndexCache>)>,
4343
/// Ids of `style` nodes.
4444
styles: Vec<NodeId>,
4545
/// Ids of `link` nodes, specifically those with the `rel` attribute value set as `stylesheet`.
4646
/// They represent the locations (URLs) of all linked stylesheet resources in the document.
4747
linked_stylesheets: Vec<NodeId>,
4848
}
4949

50+
impl fmt::Debug for Document {
51+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52+
f.debug_struct("Document")
53+
.field("nodes", &self.nodes)
54+
.field("styles", &self.styles)
55+
.field("linked_stylesheets", &self.linked_stylesheets)
56+
.finish_non_exhaustive()?;
57+
Ok(())
58+
}
59+
}
60+
5061
impl Document {
5162
pub(crate) fn parse_with_options(bytes: &[u8], preallocate_node_capacity: usize) -> Document {
5263
parser::parse_with_options(bytes, preallocate_node_capacity)
@@ -111,7 +122,7 @@ impl Document {
111122

112123
#[inline]
113124
pub(super) fn push_element_id(&mut self, node: NodeId) {
114-
self.elements.push(node);
125+
self.elements.push((node, RefCell::default()));
115126
}
116127

117128
/// Detach a node from its siblings and its parent.

css-inline/src/html/element.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,11 @@ impl<'a> Element<'a> {
7272
.next_sibling
7373
.and_then(|node_id| self.document.as_element(node_id))
7474
}
75-
pub(crate) fn matches(&self, selector: &Selector) -> bool {
76-
let mut cache = NthIndexCache::default();
75+
pub(crate) fn matches(&self, selector: &Selector, cache: &mut NthIndexCache) -> bool {
7776
let mut context = matching::MatchingContext::new(
7877
matching::MatchingMode::Normal,
7978
None,
80-
&mut cache,
79+
cache,
8180
QuirksMode::NoQuirks,
8281
matching::NeedsSelectorFlags::No,
8382
matching::IgnoreNthChildForInvalidation::No,

css-inline/src/html/iter.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use super::{
55
selectors::{ParseError, Selectors},
66
Specificity,
77
};
8+
use selectors::NthIndexCache;
9+
use std::cell::RefCell;
810

911
/// Compile selectors from a string and create an element iterator that yields elements matching these selectors.
1012
#[inline]
@@ -24,18 +26,18 @@ pub(crate) fn select<'a, 'b>(
2426
/// An internal iterator that traverses a document.
2527
struct Elements<'a> {
2628
document: &'a Document,
27-
iter: std::slice::Iter<'a, NodeId>,
29+
iter: std::slice::Iter<'a, (NodeId, RefCell<NthIndexCache>)>,
2830
}
2931

3032
impl<'a> Iterator for Elements<'a> {
31-
type Item = Element<'a>;
33+
type Item = (Element<'a>, &'a RefCell<NthIndexCache>);
3234

3335
fn next(&mut self) -> Option<Self::Item> {
34-
if let Some(element_id) = self.iter.next() {
36+
if let Some((element_id, cache)) = self.iter.next() {
3537
let NodeData::Element { element, .. } = &self.document[*element_id].data else {
3638
unreachable!("Element ids always point to element nodes")
3739
};
38-
Some(Element::new(self.document, *element_id, element))
40+
Some((Element::new(self.document, *element_id, element), cache))
3941
} else {
4042
// No more elements in the document
4143
None
@@ -64,9 +66,10 @@ impl<'a> Iterator for Select<'a> {
6466
#[inline]
6567
fn next(&mut self) -> Option<Element<'a>> {
6668
// Filter the underlying iterator to only return elements that match any of the selectors
67-
for element in self.elements.by_ref() {
69+
for (element, cache) in self.elements.by_ref() {
70+
let mut cache = cache.borrow_mut();
6871
for selector in self.selectors.iter() {
69-
if element.matches(selector) {
72+
if element.matches(selector, &mut cache) {
7073
return Some(element);
7174
}
7275
}

0 commit comments

Comments
 (0)