Skip to content

Commit 1f89042

Browse files
authored
Merge pull request #164 from adamreichold/nth-index-cache-reuse
Another try at actually using an nth index cache
2 parents c1ab88a + d8af8ea commit 1f89042

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

src/element_ref/mod.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//! Element references.
22
3+
use std::fmt;
34
use std::iter::FusedIterator;
45
use std::ops::Deref;
56

67
use ego_tree::iter::{Edge, Traverse};
78
use ego_tree::NodeRef;
89
use html5ever::serialize::{serialize, SerializeOpts, TraversalScope};
10+
use selectors::NthIndexCache;
911

1012
use crate::node::Element;
1113
use crate::{Node, Selector};
@@ -47,6 +49,7 @@ impl<'a> ElementRef<'a> {
4749
scope: *self,
4850
inner,
4951
selector,
52+
nth_index_cache: NthIndexCache::default(),
5053
}
5154
}
5255

@@ -122,11 +125,33 @@ impl<'a> Deref for ElementRef<'a> {
122125
}
123126

124127
/// Iterator over descendent elements matching a selector.
125-
#[derive(Debug, Clone)]
126128
pub struct Select<'a, 'b> {
127129
scope: ElementRef<'a>,
128130
inner: Traverse<'a, Node>,
129131
selector: &'b Selector,
132+
nth_index_cache: NthIndexCache,
133+
}
134+
135+
impl fmt::Debug for Select<'_, '_> {
136+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
137+
fmt.debug_struct("Select")
138+
.field("scope", &self.scope)
139+
.field("inner", &self.inner)
140+
.field("selector", &self.selector)
141+
.field("nth_index_cache", &"..")
142+
.finish()
143+
}
144+
}
145+
146+
impl Clone for Select<'_, '_> {
147+
fn clone(&self) -> Self {
148+
Self {
149+
scope: self.scope,
150+
inner: self.inner.clone(),
151+
selector: self.selector,
152+
nth_index_cache: NthIndexCache::default(),
153+
}
154+
}
130155
}
131156

132157
impl<'a, 'b> Iterator for Select<'a, 'b> {
@@ -136,7 +161,11 @@ impl<'a, 'b> Iterator for Select<'a, 'b> {
136161
for edge in &mut self.inner {
137162
if let Edge::Open(node) = edge {
138163
if let Some(element) = ElementRef::wrap(node) {
139-
if self.selector.matches_with_scope(&element, Some(self.scope)) {
164+
if self.selector.matches_with_scope_and_cache(
165+
&element,
166+
Some(self.scope),
167+
&mut self.nth_index_cache,
168+
) {
140169
return Some(element);
141170
}
142171
}
@@ -169,6 +198,8 @@ impl<'a> Iterator for Text<'a> {
169198
}
170199
}
171200

201+
impl FusedIterator for Text<'_> {}
202+
172203
mod element;
173204
mod serializable;
174205

src/html/mod.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
33
#[cfg(feature = "errors")]
44
use std::borrow::Cow;
5+
use std::fmt;
56
use std::iter::FusedIterator;
67

78
use ego_tree::iter::Nodes;
89
use ego_tree::Tree;
910
use html5ever::serialize::SerializeOpts;
1011
use html5ever::tree_builder::QuirksMode;
11-
use html5ever::QualName;
12-
use html5ever::{driver, serialize};
12+
use html5ever::{driver, serialize, QualName};
13+
use selectors::NthIndexCache;
1314
use tendril::TendrilSink;
1415

1516
use crate::selector::Selector;
@@ -94,6 +95,7 @@ impl Html {
9495
Select {
9596
inner: self.tree.nodes(),
9697
selector,
98+
nth_index_cache: NthIndexCache::default(),
9799
}
98100
}
99101

@@ -122,10 +124,30 @@ impl Html {
122124
}
123125

124126
/// Iterator over elements matching a selector.
125-
#[derive(Debug)]
126127
pub struct Select<'a, 'b> {
127128
inner: Nodes<'a, Node>,
128129
selector: &'b Selector,
130+
nth_index_cache: NthIndexCache,
131+
}
132+
133+
impl fmt::Debug for Select<'_, '_> {
134+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
135+
fmt.debug_struct("Select")
136+
.field("inner", &self.inner)
137+
.field("selector", &self.selector)
138+
.field("nth_index_cache", &"..")
139+
.finish()
140+
}
141+
}
142+
143+
impl Clone for Select<'_, '_> {
144+
fn clone(&self) -> Self {
145+
Self {
146+
inner: self.inner.clone(),
147+
selector: self.selector,
148+
nth_index_cache: NthIndexCache::default(),
149+
}
150+
}
129151
}
130152

131153
impl<'a, 'b> Iterator for Select<'a, 'b> {
@@ -134,7 +156,13 @@ impl<'a, 'b> Iterator for Select<'a, 'b> {
134156
fn next(&mut self) -> Option<ElementRef<'a>> {
135157
for node in self.inner.by_ref() {
136158
if let Some(element) = ElementRef::wrap(node) {
137-
if element.parent().is_some() && self.selector.matches(&element) {
159+
if element.parent().is_some()
160+
&& self.selector.matches_with_scope_and_cache(
161+
&element,
162+
None,
163+
&mut self.nth_index_cache,
164+
)
165+
{
138166
return Some(element);
139167
}
140168
}
@@ -153,7 +181,13 @@ impl<'a, 'b> DoubleEndedIterator for Select<'a, 'b> {
153181
fn next_back(&mut self) -> Option<Self::Item> {
154182
for node in self.inner.by_ref().rev() {
155183
if let Some(element) = ElementRef::wrap(node) {
156-
if element.parent().is_some() && self.selector.matches(&element) {
184+
if element.parent().is_some()
185+
&& self.selector.matches_with_scope_and_cache(
186+
&element,
187+
None,
188+
&mut self.nth_index_cache,
189+
)
190+
{
157191
return Some(element);
158192
}
159193
}

src/selector.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use html5ever::{LocalName, Namespace};
88
use selectors::{
99
matching,
1010
parser::{self, ParseRelative, SelectorList, SelectorParseErrorKind},
11+
NthIndexCache,
1112
};
1213

1314
use crate::error::SelectorErrorKind;
@@ -42,11 +43,22 @@ impl Selector {
4243
/// The optional `scope` argument is used to specify which element has `:scope` pseudo-class.
4344
/// When it is `None`, `:scope` will match the root element.
4445
pub fn matches_with_scope(&self, element: &ElementRef, scope: Option<ElementRef>) -> bool {
45-
let mut nth_index_cache = Default::default();
46+
self.matches_with_scope_and_cache(element, scope, &mut NthIndexCache::default())
47+
}
48+
49+
// The `nth_index_cache` must not be used after `self` is dropped
50+
// to avoid incorrect results (even though no undefined behaviour is possible)
51+
// due to the usage of selector memory addresses as cache keys.
52+
pub(crate) fn matches_with_scope_and_cache(
53+
&self,
54+
element: &ElementRef,
55+
scope: Option<ElementRef>,
56+
nth_index_cache: &mut NthIndexCache,
57+
) -> bool {
4658
let mut context = matching::MatchingContext::new(
4759
matching::MatchingMode::Normal,
4860
None,
49-
&mut nth_index_cache,
61+
nth_index_cache,
5062
matching::QuirksMode::NoQuirks,
5163
matching::NeedsSelectorFlags::No,
5264
matching::IgnoreNthChildForInvalidation::No,

0 commit comments

Comments
 (0)