System.Xml.NameTable optimizations (#49988)
authorkronic <kronic2006@gmail.com>
Wed, 24 Mar 2021 13:32:28 +0000 (16:32 +0300)
committerGitHub <noreply@github.com>
Wed, 24 Mar 2021 13:32:28 +0000 (09:32 -0400)
* Slight simplification and acceleration

* use SequenceEqual

* fix nullability

* innline TextEquals

* Inline ComputeHash32

* remove using

* fix nullability

Co-authored-by: kronic <kronic@softland.ru>
src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs

index cc970a4..e2d7fb2 100644 (file)
@@ -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>
@@ -19,9 +17,9 @@ namespace System.Xml
         {
             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;
@@ -32,7 +30,7 @@ namespace System.Xml
         //
         // Fields
         //
-        private Entry[] _entries;
+        private Entry?[] _entries;
         private int _count;
         private int _mask;
 
@@ -44,7 +42,7 @@ namespace System.Xml
         public NameTable()
         {
             _mask = 31;
-            _entries = new Entry[_mask + 1];
+            _entries = new Entry?[_mask + 1];
         }
 
         //
@@ -68,7 +66,7 @@ namespace System.Xml
 
             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))
                 {
@@ -104,11 +102,11 @@ namespace System.Xml
                 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;
                 }
@@ -134,7 +132,7 @@ namespace System.Xml
 
             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))
                 {
@@ -167,11 +165,11 @@ namespace System.Xml
                 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;
                 }
@@ -182,7 +180,7 @@ namespace System.Xml
 
         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))
                 {
@@ -223,17 +221,17 @@ namespace System.Xml
         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;
@@ -243,30 +241,5 @@ namespace System.Xml
             _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));
-        }
     }
 }