Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Xml.NameTable optimizations #49988

Merged
merged 7 commits into from
Mar 24, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 17 additions & 44 deletions src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

namespace System.Xml
{
/// <devdoc>
Expand All @@ -19,9 +17,9 @@ private class Entry
{
internal string str;
internal int hashCode;
internal Entry next;
internal Entry? next;

internal Entry(string str, int hashCode, Entry next)
internal Entry(string str, int hashCode, Entry? next)
{
this.str = str;
this.hashCode = hashCode;
Expand All @@ -32,7 +30,7 @@ internal Entry(string str, int hashCode, Entry next)
//
// Fields
//
private Entry[] _entries;
private Entry?[] _entries;
private int _count;
private int _mask;

Expand All @@ -44,7 +42,7 @@ internal Entry(string str, int hashCode, Entry next)
public NameTable()
{
_mask = 31;
_entries = new Entry[_mask + 1];
_entries = new Entry?[_mask + 1];
}

//
Expand All @@ -68,7 +66,7 @@ public override string Add(string key)

int hashCode = ComputeHash32(key);

for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next)
for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next)
{
if (e.hashCode == hashCode && e.str.Equals(key))
{
Expand Down Expand Up @@ -104,11 +102,11 @@ public override string Add(char[] key, int start, int len)
throw new ArgumentOutOfRangeException(nameof(len));
}

int hashCode = ComputeHash32(key, start, len);
int hashCode = string.GetHashCode(key.AsSpan(start, len));

for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next)
for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next)
{
if (e.hashCode == hashCode && TextEquals(e.str, key, start, len))
if (e.hashCode == hashCode && e.str.AsSpan().SequenceEqual(key.AsSpan(start, len)))
{
return e.str;
}
Expand All @@ -134,7 +132,7 @@ public override string Add(char[] key, int start, int len)

int hashCode = ComputeHash32(value);

for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next)
for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next)
{
if (e.hashCode == hashCode && e.str.Equals(value))
{
Expand Down Expand Up @@ -167,11 +165,11 @@ public override string Add(char[] key, int start, int len)
return null;
}

int hashCode = ComputeHash32(key, start, len);
int hashCode = string.GetHashCode(key.AsSpan(start, len));

for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next)
for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next)
{
if (e.hashCode == hashCode && TextEquals(e.str, key, start, len))
if (e.hashCode == hashCode && e.str.AsSpan().SequenceEqual(key.AsSpan(start, len)))
{
return e.str;
}
Expand All @@ -182,7 +180,7 @@ public override string Add(char[] key, int start, int len)

internal string GetOrAddEntry(string str, int hashCode)
{
for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next)
for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next)
{
if (e.hashCode == hashCode && e.str.Equals(str))
{
Expand Down Expand Up @@ -223,17 +221,17 @@ private string AddEntry(string str, int hashCode)
private void Grow()
{
int newMask = _mask * 2 + 1;
Entry[] oldEntries = _entries;
Entry[] newEntries = new Entry[newMask + 1];
Entry?[] oldEntries = _entries;
Entry?[] newEntries = new Entry?[newMask + 1];

// use oldEntries.Length to eliminate the range check
for (int i = 0; i < oldEntries.Length; i++)
{
Entry e = oldEntries[i];
Entry? e = oldEntries[i];
while (e != null)
{
int newIndex = e.hashCode & newMask;
Entry tmp = e.next;
Entry? tmp = e.next;
e.next = newEntries[newIndex];
newEntries[newIndex] = e;
e = tmp;
Expand All @@ -243,30 +241,5 @@ private void Grow()
_entries = newEntries;
_mask = newMask;
}

private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length)
{
if (str1.Length != str2Length)
{
return false;
}

// use array.Length to eliminate the range check
for (int i = 0; i < str1.Length; i++)
{
if (str1[i] != str2[str2Start + i])
{
return false;
}
}
return true;
}

private static int ComputeHash32(char[] key, int start, int len)
{
// We rely on string.GetHashCode(ROS<char>) being randomized.

return string.GetHashCode(key.AsSpan(start, len));
}
}
}