Skip to content

Commit 9024970

Browse files
committed
perf: Avoid iterating over non-Element nodes
1 parent 242d9e2 commit 9024970

File tree

7 files changed

+39
-18
lines changed

7 files changed

+39
-18
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Performance
6+
7+
- Avoid iterating over non-Element nodes.
8+
59
## [0.11.1] - 2023-12-04
610

711
### Changed

bindings/python/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Performance
6+
7+
- Avoid iterating over non-Element nodes.
8+
59
## [0.11.1] - 2023-12-09
610

711
### Added

bindings/ruby/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Performance
6+
7+
- Avoid iterating over non-Element nodes.
8+
59
## [0.11.1] - 2023-12-09
610

711
### Changed

bindings/wasm/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Performance
6+
7+
- Avoid iterating over non-Element nodes.
8+
59
## [0.11.1] - 2023-12-09
610

711
### Changed

css-inline/src/html/document.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ use std::{io::Write, iter::successors};
3838
#[derive(Debug)]
3939
pub(crate) struct Document {
4040
pub(crate) nodes: Vec<Node>,
41+
/// Ids of Element nodes.
42+
pub(crate) elements: Vec<NodeId>,
4143
/// Ids of `style` nodes.
4244
styles: Vec<NodeId>,
4345
/// Ids of `link` nodes, specifically those with the `rel` attribute value set as `stylesheet`.
@@ -57,6 +59,7 @@ impl Document {
5759
nodes.reserve(capacity);
5860
Document {
5961
nodes,
62+
elements: Vec::with_capacity(capacity),
6063
styles: Vec::new(),
6164
linked_stylesheets: Vec::new(),
6265
}
@@ -106,6 +109,11 @@ impl Document {
106109
NodeId::new(next_index)
107110
}
108111

112+
#[inline]
113+
pub(super) fn push_element_id(&mut self, node: NodeId) {
114+
self.elements.push(node);
115+
}
116+
109117
/// Detach a node from its siblings and its parent.
110118
///
111119
/// Before:

css-inline/src/html/iter.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use super::{
22
document::Document,
33
element::Element,
4-
node::{Node, NodeData, NodeId},
4+
node::{NodeData, NodeId},
55
selectors::{ParseError, Selectors},
66
Specificity,
77
};
8-
use std::iter::{Enumerate, Skip};
98

109
/// Compile selectors from a string and create an element iterator that yields elements matching these selectors.
1110
#[inline]
@@ -16,8 +15,7 @@ pub(crate) fn select<'a, 'b>(
1615
Selectors::compile(selectors).map(|selectors| Select {
1716
elements: Elements {
1817
document,
19-
// Skip the dummy & document nodes
20-
iter: document.nodes.iter().enumerate().skip(2),
18+
iter: document.elements.iter(),
2119
},
2220
selectors,
2321
})
@@ -26,24 +24,21 @@ pub(crate) fn select<'a, 'b>(
2624
/// An internal iterator that traverses a document.
2725
struct Elements<'a> {
2826
document: &'a Document,
29-
iter: Skip<Enumerate<std::slice::Iter<'a, Node>>>,
27+
iter: std::slice::Iter<'a, NodeId>,
3028
}
3129

3230
impl<'a> Iterator for Elements<'a> {
3331
type Item = Element<'a>;
3432

3533
fn next(&mut self) -> Option<Self::Item> {
36-
// Loop until we either run out of nodes or find an element node
37-
loop {
38-
if let Some((id, node)) = self.iter.next() {
39-
// If the current node is an element node, return it, else continue with the loop
40-
if let NodeData::Element { element, .. } = &node.data {
41-
return Some(Element::new(self.document, NodeId::new(id), element));
42-
}
43-
} else {
44-
// No more elements in the document
45-
return None;
46-
}
34+
if let Some(element_id) = self.iter.next() {
35+
let NodeData::Element { element, .. } = &self.document[*element_id].data else {
36+
unreachable!("Element ids always point to element nodes")
37+
};
38+
Some(Element::new(self.document, *element_id, element))
39+
} else {
40+
// No more elements in the document
41+
None
4742
}
4843
}
4944
}

css-inline/src/html/parser.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ impl Sink {
4040
attributes: Vec<Attribute>,
4141
inlining_ignored: bool,
4242
) -> NodeId {
43-
self.push_node(NodeData::Element {
43+
let node_id = self.push_node(NodeData::Element {
4444
element: ElementData::new(name, attributes),
4545
inlining_ignored,
46-
})
46+
});
47+
self.document.push_element_id(node_id);
48+
node_id
4749
}
4850

4951
fn push_text(&mut self, text: StrTendril) -> NodeId {

0 commit comments

Comments
 (0)