FastResourceComparer.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. /*============================================================
  5. **
  6. **
  7. **
  8. **
  9. **
  10. ** Purpose: A collection of quick methods for comparing
  11. ** resource keys (strings)
  12. **
  13. **
  14. ===========================================================*/
  15. #nullable enable
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Diagnostics.CodeAnalysis;
  20. namespace System.Resources
  21. {
  22. internal sealed class FastResourceComparer : IComparer, IEqualityComparer, IComparer<string?>, IEqualityComparer<string?>
  23. {
  24. internal static readonly FastResourceComparer Default = new FastResourceComparer();
  25. // Implements IHashCodeProvider too, due to Hashtable requirements.
  26. public int GetHashCode(object key)
  27. {
  28. string s = (string)key;
  29. return FastResourceComparer.HashFunction(s);
  30. }
  31. public int GetHashCode([DisallowNull] string? key)
  32. {
  33. return FastResourceComparer.HashFunction(key!);
  34. }
  35. // This hash function MUST be publically documented with the resource
  36. // file format, AND we may NEVER change this hash function's return
  37. // value (without changing the file format).
  38. internal static int HashFunction(string key)
  39. {
  40. // Never change this hash function. We must standardize it so that
  41. // others can read & write our .resources files. Additionally, we
  42. // have a copy of it in InternalResGen as well.
  43. uint hash = 5381;
  44. for (int i = 0; i < key.Length; i++)
  45. hash = ((hash << 5) + hash) ^ key[i];
  46. return (int)hash;
  47. }
  48. // Compares Strings quickly in a case-sensitive way
  49. public int Compare(object? a, object? b)
  50. {
  51. if (a == b) return 0;
  52. string? sa = (string?)a;
  53. string? sb = (string?)b;
  54. return string.CompareOrdinal(sa, sb);
  55. }
  56. public int Compare(string? a, string? b)
  57. {
  58. return string.CompareOrdinal(a, b);
  59. }
  60. public bool Equals(string? a, string? b)
  61. {
  62. return string.Equals(a, b);
  63. }
  64. public new bool Equals(object? a, object? b)
  65. {
  66. if (a == b) return true;
  67. string? sa = (string?)a;
  68. string? sb = (string?)b;
  69. return string.Equals(sa, sb);
  70. }
  71. // Input is one string to compare with, and a byte[] containing chars in
  72. // little endian unicode. Pass in the number of valid chars.
  73. public static unsafe int CompareOrdinal(string a, byte[] bytes, int bCharLength)
  74. {
  75. Debug.Assert(a != null && bytes != null, "FastResourceComparer::CompareOrdinal must have non-null params");
  76. Debug.Assert(bCharLength * 2 <= bytes.Length, "FastResourceComparer::CompareOrdinal - numChars is too big!");
  77. // This is a managed version of strcmp, but I can't take advantage
  78. // of a terminating 0, unlike strcmp in C.
  79. int i = 0;
  80. int r = 0;
  81. // Compare the min length # of characters, then return length diffs.
  82. int numChars = a.Length;
  83. if (numChars > bCharLength)
  84. numChars = bCharLength;
  85. if (bCharLength == 0) // Can't use fixed on a 0-element array.
  86. return (a.Length == 0) ? 0 : -1;
  87. fixed (byte* pb = bytes)
  88. {
  89. byte* pChar = pb;
  90. while (i < numChars && r == 0)
  91. {
  92. // little endian format
  93. int b = pChar[0] | pChar[1] << 8;
  94. r = a[i++] - b;
  95. pChar += sizeof(char);
  96. }
  97. }
  98. if (r != 0) return r;
  99. return a.Length - bCharLength;
  100. }
  101. public static int CompareOrdinal(byte[] bytes, int aCharLength, string b)
  102. {
  103. return -CompareOrdinal(b, bytes, aCharLength);
  104. }
  105. // This method is to handle potentially misaligned data accesses.
  106. // The byte* must point to little endian Unicode characters.
  107. internal static unsafe int CompareOrdinal(byte* a, int byteLen, string b)
  108. {
  109. Debug.Assert((byteLen & 1) == 0, "CompareOrdinal is expecting a UTF-16 string length, which must be even!");
  110. Debug.Assert(a != null && b != null, "Null args not allowed.");
  111. Debug.Assert(byteLen >= 0, "byteLen must be non-negative.");
  112. int r = 0;
  113. int i = 0;
  114. // Compare the min length # of characters, then return length diffs.
  115. int numChars = byteLen >> 1;
  116. if (numChars > b.Length)
  117. numChars = b.Length;
  118. while (i < numChars && r == 0)
  119. {
  120. // Must compare character by character, not byte by byte.
  121. char aCh = (char)(*a++ | (*a++ << 8));
  122. r = aCh - b[i++];
  123. }
  124. if (r != 0) return r;
  125. return byteLen - b.Length * 2;
  126. }
  127. }
  128. }