You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.3 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. namespace Newtonsoft.Json.Utilities
  27. {
  28. internal class PropertyNameTable
  29. {
  30. // used to defeat hashtable DoS attack where someone passes in lots of strings that hash to the same hash code
  31. private static readonly int HashCodeRandomizer;
  32. private int _count;
  33. private Entry[] _entries;
  34. private int _mask = 31;
  35. static PropertyNameTable()
  36. {
  37. HashCodeRandomizer = Environment.TickCount;
  38. }
  39. public PropertyNameTable()
  40. {
  41. _entries = new Entry[_mask + 1];
  42. }
  43. public string Get(char[] key, int start, int length)
  44. {
  45. if (length == 0)
  46. {
  47. return string.Empty;
  48. }
  49. int hashCode = length + HashCodeRandomizer;
  50. hashCode += (hashCode << 7) ^ key[start];
  51. int end = start + length;
  52. for (int i = start + 1; i < end; i++)
  53. {
  54. hashCode += (hashCode << 7) ^ key[i];
  55. }
  56. hashCode -= hashCode >> 17;
  57. hashCode -= hashCode >> 11;
  58. hashCode -= hashCode >> 5;
  59. for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next)
  60. {
  61. if (entry.HashCode == hashCode && TextEquals(entry.Value, key, start, length))
  62. {
  63. return entry.Value;
  64. }
  65. }
  66. return null;
  67. }
  68. public string Add(string key)
  69. {
  70. if (key == null)
  71. {
  72. throw new ArgumentNullException(nameof(key));
  73. }
  74. int length = key.Length;
  75. if (length == 0)
  76. {
  77. return string.Empty;
  78. }
  79. int hashCode = length + HashCodeRandomizer;
  80. for (int i = 0; i < key.Length; i++)
  81. {
  82. hashCode += (hashCode << 7) ^ key[i];
  83. }
  84. hashCode -= hashCode >> 17;
  85. hashCode -= hashCode >> 11;
  86. hashCode -= hashCode >> 5;
  87. for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next)
  88. {
  89. if (entry.HashCode == hashCode && entry.Value.Equals(key))
  90. {
  91. return entry.Value;
  92. }
  93. }
  94. return AddEntry(key, hashCode);
  95. }
  96. private string AddEntry(string str, int hashCode)
  97. {
  98. int index = hashCode & _mask;
  99. Entry entry = new Entry(str, hashCode, _entries[index]);
  100. _entries[index] = entry;
  101. if (_count++ == _mask)
  102. {
  103. Grow();
  104. }
  105. return entry.Value;
  106. }
  107. private void Grow()
  108. {
  109. Entry[] entries = _entries;
  110. int newMask = (_mask * 2) + 1;
  111. Entry[] newEntries = new Entry[newMask + 1];
  112. for (int i = 0; i < entries.Length; i++)
  113. {
  114. Entry next;
  115. for (Entry entry = entries[i]; entry != null; entry = next)
  116. {
  117. int index = entry.HashCode & newMask;
  118. next = entry.Next;
  119. entry.Next = newEntries[index];
  120. newEntries[index] = entry;
  121. }
  122. }
  123. _entries = newEntries;
  124. _mask = newMask;
  125. }
  126. private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length)
  127. {
  128. if (str1.Length != str2Length)
  129. {
  130. return false;
  131. }
  132. for (int i = 0; i < str1.Length; i++)
  133. {
  134. if (str1[i] != str2[str2Start + i])
  135. {
  136. return false;
  137. }
  138. }
  139. return true;
  140. }
  141. private class Entry
  142. {
  143. internal readonly string Value;
  144. internal readonly int HashCode;
  145. internal Entry Next;
  146. internal Entry(string value, int hashCode, Entry next)
  147. {
  148. Value = value;
  149. HashCode = hashCode;
  150. Next = next;
  151. }
  152. }
  153. }
  154. }