Skip to content

Commit 843d527

Browse files
committed
Improve ChildSelector performance
1 parent c80b834 commit 843d527

File tree

7 files changed

+233
-81
lines changed

7 files changed

+233
-81
lines changed

src/AngleSharp/Css/Dom/Internal/FirstChildSelector.cs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,38 @@ public Boolean Match(IElement element, IElement? scope)
2121

2222
if (parent != null)
2323
{
24-
var n = Math.Sign(Step);
25-
var k = 0;
24+
// remove interface dispatch overhead
25+
if (parent.ChildNodes is NodeList nodeList)
26+
{
27+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element, scope);
28+
}
29+
30+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element, scope);
31+
}
32+
33+
return false;
34+
}
35+
36+
private Boolean DoMatch<T>(T nodes, IElement element, IElement? scope) where T : INodeListAccessor
37+
{
38+
var step = Step;
39+
var n = Math.Sign(step);
40+
var k = 0;
41+
var kind = Kind;
42+
var matchAll = ReferenceEquals(Kind, AllSelector.Instance);
43+
var offset = Offset;
44+
var length = nodes.Length;
2645

27-
for (var i = 0; i < parent.ChildNodes.Length; i++)
46+
for (var i = 0; i < length; i++)
47+
{
48+
if (nodes[i] is IElement child && (matchAll || kind.Match(child, scope)))
2849
{
29-
if (parent.ChildNodes[i] is IElement child && Kind.Match(child, scope))
30-
{
31-
k += 1;
50+
k += 1;
3251

33-
if (child == element)
34-
{
35-
var diff = k - Offset;
36-
return diff == 0 || (Math.Sign(diff) == n && diff % Step == 0);
37-
}
52+
if (child == element)
53+
{
54+
var diff = k - offset;
55+
return diff == 0 || (Math.Sign(diff) == n && diff % step == 0);
3856
}
3957
}
4058
}

src/AngleSharp/Css/Dom/Internal/FirstColumnSelector.cs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,51 @@ public Boolean Match(IElement element, IElement? scope)
2020

2121
if (parent != null)
2222
{
23-
var n = Math.Sign(Step);
24-
var k = 0;
23+
// remove interface dispatch overhead
24+
if (parent.ChildNodes is NodeList nodeList)
25+
{
26+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element);
27+
}
28+
29+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element);
30+
}
31+
32+
return false;
33+
}
2534

26-
for (var i = 0; i < parent.ChildNodes.Length; i++)
35+
private Boolean DoMatch<T>(T nodes, IElement element) where T : INodeListAccessor
36+
{
37+
var step = Step;
38+
var n = Math.Sign(step);
39+
var k = 0;
40+
var offset = Offset;
41+
var length = nodes.Length;
42+
43+
for (var i = 0; i < length; i++)
44+
{
45+
if (nodes[i] is IHtmlTableCellElement child)
2746
{
28-
if (parent.ChildNodes[i] is IHtmlTableCellElement child)
47+
var span = child.ColumnSpan;
48+
k += span;
49+
50+
if (child == element)
2951
{
30-
var span = child.ColumnSpan;
31-
k += span;
52+
var diff = k - offset;
3253

33-
if (child == element)
54+
for (var index = 0; index < span; index++, diff--)
3455
{
35-
var diff = k - Offset;
36-
37-
for (var index = 0; index < span; index++, diff--)
56+
if (diff == 0 || (Math.Sign(diff) == n && diff % step == 0))
3857
{
39-
if (diff == 0 || (Math.Sign(diff) == n && diff % Step == 0))
40-
{
41-
return true;
42-
}
58+
return true;
4359
}
44-
45-
return false;
4660
}
61+
62+
return false;
4763
}
4864
}
4965
}
5066

5167
return false;
5268
}
5369
}
54-
}
70+
}

src/AngleSharp/Css/Dom/Internal/FirstTypeSelector.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,42 @@ public Boolean Match(IElement element, IElement? scope)
2020

2121
if (parent != null)
2222
{
23-
var n = Math.Sign(Step);
24-
var k = 0;
23+
// remove interface dispatch overhead
24+
if (parent.ChildNodes is NodeList nodeList)
25+
{
26+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element);
27+
}
28+
29+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element);
30+
}
31+
32+
return false;
33+
}
34+
35+
private Boolean DoMatch<T>(T nodes, IElement element) where T : INodeListAccessor
36+
{
37+
var k = 0;
38+
var step = Step;
39+
var n = Math.Sign(step);
40+
var offset = Offset;
41+
var length = nodes.Length;
42+
var nodeName = element.NodeName;
2543

26-
for (var i = 0; i < parent.ChildNodes.Length; i++)
44+
for (var i = 0; i < length; i++)
45+
{
46+
if (nodes[i] is IElement child && child.NodeName.Is(nodeName))
2747
{
28-
if (parent.ChildNodes[i] is IElement child && child.NodeName.Is(element.NodeName))
29-
{
30-
k += 1;
48+
k += 1;
3149

32-
if (child == element)
33-
{
34-
var diff = k - Offset;
35-
return diff == 0 || (Math.Sign(diff) == n && diff % Step == 0);
36-
}
50+
if (child == element)
51+
{
52+
var diff = k - offset;
53+
return diff == 0 || (Math.Sign(diff) == n && diff % step == 0);
3754
}
3855
}
3956
}
4057

4158
return false;
4259
}
4360
}
44-
}
61+
}

src/AngleSharp/Css/Dom/Internal/LastChildSelector.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,42 @@ public Boolean Match(IElement element, IElement? scope)
2121

2222
if (parent != null)
2323
{
24-
var n = Math.Sign(Step);
25-
var k = 0;
24+
// remove interface dispatch overhead
25+
if (parent.ChildNodes is NodeList nodeList)
26+
{
27+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element, scope);
28+
}
29+
30+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element, scope);
31+
}
32+
33+
return false;
34+
}
35+
36+
private Boolean DoMatch<T>(T nodes, IElement element, IElement? scope) where T : INodeListAccessor
37+
{
38+
var step = Step;
39+
var n = Math.Sign(step);
40+
var k = 0;
41+
var kind = Kind;
42+
var matchAll = ReferenceEquals(Kind, AllSelector.Instance);
43+
var offset = Offset;
2644

27-
for (var i = parent.ChildNodes.Length - 1; i >= 0; i--)
45+
for (var i = nodes.Length - 1; i >= 0; i--)
46+
{
47+
if (nodes[i] is IElement child && (matchAll || kind.Match(child, scope)))
2848
{
29-
if (parent.ChildNodes[i] is IElement child && Kind.Match(child, scope))
30-
{
31-
k += 1;
49+
k += 1;
3250

33-
if (child == element)
34-
{
35-
var diff = k - Offset;
36-
return diff == 0 || (Math.Sign(diff) == n && diff % Step == 0);
37-
}
51+
if (child == element)
52+
{
53+
var diff = k - offset;
54+
return diff == 0 || (Math.Sign(diff) == n && diff % step == 0);
3855
}
3956
}
4057
}
4158

4259
return false;
4360
}
4461
}
45-
}
62+
}

src/AngleSharp/Css/Dom/Internal/LastColumnSelector.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,50 @@ public Boolean Match(IElement element, IElement? scope)
2020

2121
if (parent != null)
2222
{
23-
var n = Math.Sign(Step);
24-
var k = 0;
23+
// remove interface dispatch overhead
24+
if (parent.ChildNodes is NodeList nodeList)
25+
{
26+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element);
27+
}
28+
29+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element);
30+
}
31+
32+
return false;
33+
}
2534

26-
for (var i = parent.ChildNodes.Length - 1; i >= 0; i--)
35+
private Boolean DoMatch<T>(T nodes, IElement element) where T : INodeListAccessor
36+
{
37+
var step = Step;
38+
var n = Math.Sign(step);
39+
var k = 0;
40+
var offset = Offset;
41+
42+
for (var i = nodes.Length - 1; i >= 0; i--)
43+
{
44+
if (nodes[i] is IHtmlTableCellElement child)
2745
{
28-
if (parent.ChildNodes[i] is IHtmlTableCellElement child)
46+
var span = child.ColumnSpan;
47+
k += span;
48+
49+
if (child == element)
2950
{
30-
var span = child.ColumnSpan;
31-
k += span;
51+
var diff = k - offset;
3252

33-
if (child == element)
53+
for (var index = 0; index < span; index++, diff--)
3454
{
35-
var diff = k - Offset;
36-
37-
for (var index = 0; index < span; index++, diff--)
55+
if (diff == 0 || (Math.Sign(diff) == n && diff % step == 0))
3856
{
39-
if (diff == 0 || (Math.Sign(diff) == n && diff % Step == 0))
40-
{
41-
return true;
42-
}
57+
return true;
4358
}
44-
45-
return false;
4659
}
60+
61+
return false;
4762
}
4863
}
4964
}
5065

5166
return false;
5267
}
5368
}
54-
}
69+
}

src/AngleSharp/Css/Dom/Internal/LastTypeSelector.cs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,36 @@ public Boolean Match(IElement element, IElement? scope)
2020

2121
if (parent != null)
2222
{
23-
var n = Math.Sign(Step);
24-
var k = 0;
23+
// remove interface dispatch overhead
24+
if (parent.ChildNodes is NodeList nodeList)
25+
{
26+
return DoMatch(new ConcreteNodeListAccessor(nodeList), element);
27+
}
28+
29+
return DoMatch(new InterfaceNodeListAccessor(parent.ChildNodes), element);
30+
}
31+
32+
return false;
33+
}
34+
35+
private Boolean DoMatch<T>(T nodes, IElement element) where T : INodeListAccessor
36+
{
37+
var step = Step;
38+
var n = Math.Sign(step);
39+
var k = 0;
40+
var offset = Offset;
41+
var nodeName = element.NodeName;
2542

26-
for (var i = parent.ChildNodes.Length - 1; i >= 0; i--)
43+
for (var i = nodes.Length - 1; i >= 0; i--)
44+
{
45+
if (nodes[i] is IElement child && child.NodeName.Is(nodeName))
2746
{
28-
if (parent.ChildNodes[i] is IElement child && child.NodeName.Is(element.NodeName))
29-
{
30-
k += 1;
47+
k += 1;
3148

32-
if (child == element)
33-
{
34-
var diff = k - Offset;
35-
return diff == 0 || (Math.Sign(diff) == n && diff % Step == 0);
36-
}
49+
if (child == element)
50+
{
51+
var diff = k - offset;
52+
return diff == 0 || (Math.Sign(diff) == n && diff % step == 0);
3753
}
3854
}
3955
}

0 commit comments

Comments
 (0)