NameTable.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //------------------------------------------------------------------------------
  2. // <copyright file="NameTable.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. //------------------------------------------------------------------------------
  7. using System;
  8. namespace System.Xml {
  9. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable"]/*' />
  10. /// <devdoc>
  11. /// <para>
  12. /// XmlNameTable implemented as a simple hash table.
  13. /// </para>
  14. /// </devdoc>
  15. public class NameTable : XmlNameTable {
  16. //
  17. // Private types
  18. //
  19. class Entry {
  20. internal string str;
  21. internal int hashCode;
  22. internal Entry next;
  23. internal Entry( string str, int hashCode, Entry next ) {
  24. this.str = str;
  25. this.hashCode = hashCode;
  26. this.next = next;
  27. }
  28. }
  29. //
  30. // Fields
  31. //
  32. Entry[] entries;
  33. int count;
  34. int mask;
  35. int hashCodeRandomizer;
  36. //
  37. // Constructor
  38. //
  39. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable.NameTable"]/*' />
  40. /// <devdoc>
  41. /// Public constructor.
  42. /// </devdoc>
  43. public NameTable() {
  44. mask = 31;
  45. entries = new Entry[mask+1];
  46. hashCodeRandomizer = Environment.TickCount;
  47. }
  48. //
  49. // XmlNameTable public methods
  50. //
  51. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable.Add"]/*' />
  52. /// <devdoc>
  53. /// Add the given string to the NameTable or return
  54. /// the existing string if it is already in the NameTable.
  55. /// </devdoc>
  56. public override string Add( string key ) {
  57. if ( key == null ) {
  58. throw new ArgumentNullException( "key" );
  59. }
  60. int len = key.Length;
  61. if ( len == 0 ) {
  62. return string.Empty;
  63. }
  64. int hashCode = len + hashCodeRandomizer;
  65. // use key.Length to eliminate the rangecheck
  66. for ( int i = 0; i < key.Length; i++ ) {
  67. hashCode += ( hashCode << 7 ) ^ key[i];
  68. }
  69. // mix it a bit more
  70. hashCode -= hashCode >> 17;
  71. hashCode -= hashCode >> 11;
  72. hashCode -= hashCode >> 5;
  73. for ( Entry e = entries[hashCode & mask]; e != null; e = e.next ) {
  74. if ( e.hashCode == hashCode && e.str.Equals( key ) ) {
  75. return e.str;
  76. }
  77. }
  78. return AddEntry( key, hashCode );
  79. }
  80. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable.Add1"]/*' />
  81. /// <devdoc>
  82. /// Add the given string to the NameTable or return
  83. /// the existing string if it is already in the NameTable.
  84. /// </devdoc>
  85. public override string Add( char[] key, int start, int len ) {
  86. if ( len == 0 ) {
  87. return string.Empty;
  88. }
  89. int hashCode = len + hashCodeRandomizer;
  90. hashCode += ( hashCode << 7 ) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid
  91. int end = start+len;
  92. for ( int i = start + 1; i < end; i++) {
  93. hashCode += ( hashCode << 7 ) ^ key[i];
  94. }
  95. // mix it a bit more
  96. hashCode -= hashCode >> 17;
  97. hashCode -= hashCode >> 11;
  98. hashCode -= hashCode >> 5;
  99. for ( Entry e = entries[hashCode & mask]; e != null; e = e.next ) {
  100. if ( e.hashCode == hashCode && TextEquals( e.str, key, start, len ) ) {
  101. return e.str;
  102. }
  103. }
  104. return AddEntry( new string( key, start, len ), hashCode );
  105. }
  106. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable.Get"]/*' />
  107. /// <devdoc>
  108. /// Find the matching string in the NameTable.
  109. /// </devdoc>
  110. public override string Get( string value ) {
  111. if ( value == null ) {
  112. throw new ArgumentNullException("value");
  113. }
  114. if ( value.Length == 0 ) {
  115. return string.Empty;
  116. }
  117. int len = value.Length + hashCodeRandomizer;
  118. int hashCode = len;
  119. // use value.Length to eliminate the rangecheck
  120. for ( int i = 0; i < value.Length; i++ ) {
  121. hashCode += ( hashCode << 7 ) ^ value[i];
  122. }
  123. // mix it a bit more
  124. hashCode -= hashCode >> 17;
  125. hashCode -= hashCode >> 11;
  126. hashCode -= hashCode >> 5;
  127. for ( Entry e = entries[hashCode & mask]; e != null; e = e.next ) {
  128. if ( e.hashCode == hashCode && e.str.Equals( value ) ) {
  129. return e.str;
  130. }
  131. }
  132. return null;
  133. }
  134. /// <include file='doc\NameTable.uex' path='docs/doc[@for="NameTable.Get1"]/*' />
  135. /// <devdoc>
  136. /// Find the matching string atom given a range of
  137. /// characters.
  138. /// </devdoc>
  139. public override string Get( char[] key, int start, int len ) {
  140. if ( len == 0 ) {
  141. return string.Empty;
  142. }
  143. int hashCode = len + hashCodeRandomizer;
  144. hashCode += ( hashCode << 7 ) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid
  145. int end = start+len;
  146. for ( int i = start + 1; i < end; i++) {
  147. hashCode += ( hashCode << 7 ) ^ key[i];
  148. }
  149. // mix it a bit more
  150. hashCode -= hashCode >> 17;
  151. hashCode -= hashCode >> 11;
  152. hashCode -= hashCode >> 5;
  153. for ( Entry e = entries[hashCode & mask]; e != null; e = e.next ) {
  154. if ( e.hashCode == hashCode && TextEquals( e.str, key, start, len ) ) {
  155. return e.str;
  156. }
  157. }
  158. return null;
  159. }
  160. //
  161. // Private methods
  162. //
  163. private string AddEntry( string str, int hashCode ) {
  164. int index = hashCode & mask;
  165. Entry e = new Entry( str, hashCode, entries[index] );
  166. entries[index] = e;
  167. if ( count++ == mask ) {
  168. Grow();
  169. }
  170. return e.str;
  171. }
  172. private void Grow() {
  173. int newMask = mask * 2 + 1;
  174. Entry[] oldEntries = entries;
  175. Entry[] newEntries = new Entry[newMask+1];
  176. // use oldEntries.Length to eliminate the rangecheck
  177. for ( int i = 0; i < oldEntries.Length; i++ ) {
  178. Entry e = oldEntries[i];
  179. while ( e != null ) {
  180. int newIndex = e.hashCode & newMask;
  181. Entry tmp = e.next;
  182. e.next = newEntries[newIndex];
  183. newEntries[newIndex] = e;
  184. e = tmp;
  185. }
  186. }
  187. entries = newEntries;
  188. mask = newMask;
  189. }
  190. private static bool TextEquals( string str1, char[] str2, int str2Start, int str2Length ) {
  191. if ( str1.Length != str2Length ) {
  192. return false;
  193. }
  194. // use array.Length to eliminate the rangecheck
  195. for ( int i = 0; i < str1.Length; i++ ) {
  196. if ( str1[i] != str2[str2Start+i] ) {
  197. return false;
  198. }
  199. }
  200. return true;
  201. }
  202. }
  203. }