Skip to content

Commit e0efd98

Browse files
committed
Test for an unsafe get method for performance.
1 parent 2b0af54 commit e0efd98

2 files changed

Lines changed: 55 additions & 17 deletions

File tree

src/MrKWatkins.Ast.Tests/ChildrenTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,12 @@ public void IndexOf()
310310
[Test]
311311
public void Insert()
312312
{
313-
var child1 = new BNode();
314-
var child2 = new BNode();
313+
var child1 = new BNode { Name = "Child 1" };
314+
var child2 = new BNode { Name = "Child 2" };
315315

316316
var parent = new ANode(child1, child2);
317317

318-
var child3 = new BNode();
318+
var child3 = new BNode { Name = "Child 3" };
319319
parent.Children.Insert(1, child3);
320320

321321
child3.Parent.Should().BeSameAs(parent);
@@ -325,9 +325,9 @@ public void Insert()
325325
[Test]
326326
public void RemoveAt()
327327
{
328-
var child1 = new BNode();
329-
var child2 = new BNode();
330-
var child3 = new BNode();
328+
var child1 = new BNode { Name = "Child 1" };
329+
var child2 = new BNode { Name = "Child 2" };
330+
var child3 = new BNode { Name = "Child 3" };
331331

332332
var parent = new ANode(child1, child2, child3);
333333

src/MrKWatkins.Ast/Children.cs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
14
namespace MrKWatkins.Ast;
25

36
/// <summary>
@@ -11,19 +14,19 @@ namespace MrKWatkins.Ast;
1114
public sealed partial class Children<TNode> : IList<TNode>
1215
where TNode : Node<TNode>
1316
{
14-
private readonly List<TNode> nodes;
1517
private readonly TNode parent;
18+
private TNode[] nodes;
1619

1720
internal Children(TNode parent)
1821
{
1922
this.parent = parent;
20-
nodes = new List<TNode>();
23+
nodes = Array.Empty<TNode>();
2124
}
2225

2326
internal Children(TNode parent, [InstantHandle] IEnumerable<TNode> nodes)
2427
{
2528
this.parent = parent;
26-
this.nodes = nodes.ToList();
29+
this.nodes = nodes.ToArray();
2730
foreach (var node in this.nodes)
2831
{
2932
node.Parent = parent;
@@ -38,7 +41,11 @@ internal Children(TNode parent, [InstantHandle] IEnumerable<TNode> nodes)
3841
public void Add(TNode node)
3942
{
4043
node.Parent = parent;
41-
nodes.Add(node);
44+
45+
var newNodes = new TNode[nodes.Length + 1];
46+
Array.Copy(nodes, newNodes, nodes.Length);
47+
newNodes[^1] = node;
48+
nodes = newNodes;
4249
}
4350

4451
/// <summary>
@@ -73,7 +80,7 @@ public void Clear()
7380
node.RemoveParent();
7481
}
7582

76-
nodes.Clear();
83+
nodes = Array.Empty<TNode>();
7784
}
7885

7986
/// <summary>
@@ -92,9 +99,10 @@ public void Clear()
9299
/// <returns><c>true</c> if <paramref name="node" /> was in the collection and was removed, <c>false</c> otherwise.</returns>
93100
public bool Remove(TNode node)
94101
{
95-
if (nodes.Remove(node))
102+
var index = IndexOf(node);
103+
if (index != -1)
96104
{
97-
node.RemoveParent();
105+
RemoveAt(index);
98106
return true;
99107
}
100108

@@ -146,7 +154,11 @@ public void Move([InstantHandle] IEnumerable<TNode> nodes)
146154
/// <summary>
147155
/// The number of nodes in the collection.
148156
/// </summary>
149-
public int Count => nodes.Count;
157+
public int Count
158+
{
159+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
160+
get => nodes.Length;
161+
}
150162

151163
bool ICollection<TNode>.IsReadOnly => false;
152164

@@ -157,7 +169,7 @@ public void Move([InstantHandle] IEnumerable<TNode> nodes)
157169
/// <returns>The index of the node or -1 if it is not in the collection.</returns>
158170
public int IndexOf(TNode node) =>
159171
node.HasParent && ReferenceEquals(node.Parent, parent)
160-
? nodes.IndexOf(node)
172+
? Array.IndexOf(nodes, node)
161173
: -1;
162174

163175
/// <summary>
@@ -170,7 +182,12 @@ public int IndexOf(TNode node) =>
170182
public void Insert(int index, TNode node)
171183
{
172184
node.Parent = parent;
173-
nodes.Insert(index, node);
185+
186+
var newNodes = new TNode[nodes.Length + 1];
187+
Array.Copy(nodes, newNodes, index);
188+
Array.Copy(nodes, index, newNodes, index + 1, nodes.Length - index);
189+
newNodes[index] = node;
190+
nodes = newNodes;
174191
}
175192

176193
void IList<TNode>.RemoveAt(int index) => RemoveAt(index);
@@ -184,8 +201,18 @@ public void Insert(int index, TNode node)
184201
public TNode RemoveAt(int index)
185202
{
186203
var node = nodes[index];
204+
if (nodes.Length == 1)
205+
{
206+
nodes = Array.Empty<TNode>();
207+
}
208+
else
209+
{
210+
var newNodes = new TNode[nodes.Length - 1];
211+
Array.Copy(nodes, newNodes, index);
212+
Array.Copy(nodes, index + 1, newNodes, index, nodes.Length - index - 1);
213+
nodes = newNodes;
214+
}
187215
node.RemoveParent();
188-
nodes.RemoveAt(index);
189216
return node;
190217
}
191218

@@ -391,4 +418,15 @@ public TChild SingleOfType<TChild>()
391418

392419
return single ?? throw new InvalidOperationException($"Expected {parent.GetType().SimpleName()} to have 1 child of type {typeof(TChild).SimpleName()} but found none.");
393420
}
421+
422+
/// <summary>
423+
/// TODO
424+
/// </summary>
425+
[Pure]
426+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
427+
public TNode UnsafeGet(int index)
428+
{
429+
ref var nodesReference = ref MemoryMarshal.GetArrayDataReference(nodes);
430+
return Unsafe.Add(ref nodesReference, index);
431+
}
394432
}

0 commit comments

Comments
 (0)