HybridDictionary.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using System.Collections;
  2. using System.Runtime.CompilerServices;
  3. namespace Jint.Collections
  4. {
  5. internal class HybridDictionary<TValue> : IEnumerable<KeyValuePair<Key, TValue>>
  6. {
  7. private const int CutoverPoint = 9;
  8. private const int InitialDictionarySize = 13;
  9. private const int FixedSizeCutoverPoint = 6;
  10. private readonly bool _checkExistingKeys;
  11. private ListDictionary<TValue> _list;
  12. internal StringDictionarySlim<TValue> _dictionary;
  13. public HybridDictionary() : this(0, checkExistingKeys: true)
  14. {
  15. }
  16. public HybridDictionary(int initialSize, bool checkExistingKeys)
  17. {
  18. _checkExistingKeys = checkExistingKeys;
  19. if (initialSize >= FixedSizeCutoverPoint)
  20. {
  21. _dictionary = new StringDictionarySlim<TValue>(initialSize);
  22. }
  23. }
  24. public TValue this[Key key]
  25. {
  26. get
  27. {
  28. TryGetValue(key, out var value);
  29. return value;
  30. }
  31. set
  32. {
  33. if (_dictionary != null)
  34. {
  35. _dictionary[key] = value;
  36. }
  37. else if (_list != null)
  38. {
  39. if (_list.Count >= CutoverPoint - 1)
  40. {
  41. SwitchToDictionary(key, value);
  42. }
  43. else
  44. {
  45. _list[key] = value;
  46. }
  47. }
  48. else
  49. {
  50. _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
  51. }
  52. }
  53. }
  54. public bool TryGetValue(Key key, out TValue value)
  55. {
  56. if (_dictionary != null)
  57. {
  58. return _dictionary.TryGetValue(key, out value);
  59. }
  60. if (_list != null)
  61. {
  62. return _list.TryGetValue(key, out value);
  63. }
  64. value = default;
  65. return false;
  66. }
  67. private void SwitchToDictionary(Key key, TValue value)
  68. {
  69. var dictionary = new StringDictionarySlim<TValue>(InitialDictionarySize);
  70. foreach (var pair in _list)
  71. {
  72. dictionary[pair.Key] = pair.Value;
  73. }
  74. dictionary[key] = value;
  75. _dictionary = dictionary;
  76. _list = null;
  77. }
  78. public int Count
  79. {
  80. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  81. get => _dictionary?.Count ?? _list?.Count ?? 0;
  82. }
  83. public void Add(Key key, TValue value)
  84. {
  85. if (_dictionary != null)
  86. {
  87. _dictionary.GetOrAddValueRef(key) = value;
  88. }
  89. else
  90. {
  91. if (_list == null)
  92. {
  93. _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
  94. }
  95. else
  96. {
  97. if (_list.Count + 1 >= CutoverPoint)
  98. {
  99. SwitchToDictionary(key, value);
  100. }
  101. else
  102. {
  103. _list.Add(key, value);
  104. }
  105. }
  106. }
  107. }
  108. public void Clear()
  109. {
  110. if (_dictionary != null)
  111. {
  112. var dictionary = _dictionary;
  113. _dictionary = null;
  114. dictionary.Clear();
  115. }
  116. if (_list != null)
  117. {
  118. var cachedList = _list;
  119. _list = null;
  120. cachedList.Clear();
  121. }
  122. }
  123. public bool ContainsKey(Key key)
  124. {
  125. if (_dictionary != null)
  126. {
  127. return _dictionary.ContainsKey(key);
  128. }
  129. var cachedList = _list;
  130. if (cachedList != null)
  131. {
  132. return cachedList.ContainsKey(key);
  133. }
  134. return false;
  135. }
  136. IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator()
  137. {
  138. if (_dictionary != null)
  139. {
  140. return _dictionary.GetEnumerator();
  141. }
  142. if (_list != null)
  143. {
  144. return _list.GetEnumerator();
  145. }
  146. return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  147. }
  148. IEnumerator IEnumerable.GetEnumerator()
  149. {
  150. if (_dictionary != null)
  151. {
  152. return _dictionary.GetEnumerator();
  153. }
  154. if (_list != null)
  155. {
  156. return _list.GetEnumerator();
  157. }
  158. return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  159. }
  160. public bool Remove(Key key)
  161. {
  162. if (_dictionary != null)
  163. {
  164. return _dictionary.Remove(key);
  165. }
  166. return _list != null && _list.Remove(key);
  167. }
  168. /// <summary>
  169. /// Optimization when no need to check for existing items.
  170. /// </summary>
  171. public bool CheckExistingKeys
  172. {
  173. set
  174. {
  175. if (_list != null)
  176. {
  177. _list.CheckExistingKeys = value;
  178. }
  179. }
  180. }
  181. }
  182. }