HybridDictionary.cs 5.4 KB

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