FastResourceComparer.cs 5.1 KB

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