HybridDictionary.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. protected HybridDictionary(StringDictionarySlim<TValue> dictionary)
  26. {
  27. _checkExistingKeys = true;
  28. _dictionary = dictionary;
  29. }
  30. public TValue this[Key key]
  31. {
  32. get
  33. {
  34. TryGetValue(key, out var value);
  35. return value;
  36. }
  37. set
  38. {
  39. if (_dictionary != null)
  40. {
  41. _dictionary[key] = value;
  42. }
  43. else if (_list != null)
  44. {
  45. if (_list.Count >= CutoverPoint - 1)
  46. {
  47. SwitchToDictionary(key, value, tryAdd: false);
  48. }
  49. else
  50. {
  51. _list[key] = value;
  52. }
  53. }
  54. else
  55. {
  56. _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
  57. }
  58. }
  59. }
  60. public bool TryGetValue(Key key, out TValue value)
  61. {
  62. if (_dictionary != null)
  63. {
  64. return _dictionary.TryGetValue(key, out value);
  65. }
  66. if (_list != null)
  67. {
  68. return _list.TryGetValue(key, out value);
  69. }
  70. value = default;
  71. return false;
  72. }
  73. public void SetOrUpdateValue<TState>(Key key, Func<TValue, TState, TValue> updater, TState state)
  74. {
  75. if (_dictionary != null)
  76. {
  77. _dictionary.SetOrUpdateValue(key, updater, state);
  78. }
  79. else if (_list != null)
  80. {
  81. _list.SetOrUpdateValue(key, updater, state);
  82. }
  83. else
  84. {
  85. _list = new ListDictionary<TValue>(key, updater(default, state), _checkExistingKeys);
  86. }
  87. }
  88. private bool SwitchToDictionary(Key key, TValue value, bool tryAdd)
  89. {
  90. var dictionary = new StringDictionarySlim<TValue>(InitialDictionarySize);
  91. foreach (var pair in _list)
  92. {
  93. dictionary[pair.Key] = pair.Value;
  94. }
  95. bool result;
  96. if (tryAdd)
  97. {
  98. result = dictionary.TryAdd(key, value);
  99. }
  100. else
  101. {
  102. dictionary[key] = value;
  103. result = true;
  104. }
  105. _dictionary = dictionary;
  106. _list = null;
  107. return result;
  108. }
  109. public int Count
  110. {
  111. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  112. get => _dictionary?.Count ?? _list?.Count ?? 0;
  113. }
  114. public bool TryAdd(Key key, TValue value)
  115. {
  116. if (_dictionary != null)
  117. {
  118. return _dictionary.TryAdd(key, value);
  119. }
  120. else
  121. {
  122. _list ??= new ListDictionary<TValue>(key, value, _checkExistingKeys);
  123. if (_list.Count + 1 >= CutoverPoint)
  124. {
  125. return SwitchToDictionary(key, value, tryAdd: true);
  126. }
  127. else
  128. {
  129. return _list.Add(key, value, tryAdd: true);
  130. }
  131. }
  132. }
  133. public void Add(Key key, TValue value)
  134. {
  135. if (_dictionary != null)
  136. {
  137. _dictionary.GetOrAddValueRef(key) = value;
  138. }
  139. else
  140. {
  141. if (_list == null)
  142. {
  143. _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
  144. }
  145. else
  146. {
  147. if (_list.Count + 1 >= CutoverPoint)
  148. {
  149. SwitchToDictionary(key, value, tryAdd: false);
  150. }
  151. else
  152. {
  153. _list.Add(key, value);
  154. }
  155. }
  156. }
  157. }
  158. public void Clear()
  159. {
  160. _dictionary?.Clear();
  161. _list?.Clear();
  162. }
  163. public bool ContainsKey(Key key)
  164. {
  165. if (_dictionary != null)
  166. {
  167. return _dictionary.ContainsKey(key);
  168. }
  169. if (_list != null)
  170. {
  171. return _list.ContainsKey(key);
  172. }
  173. return false;
  174. }
  175. IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator()
  176. {
  177. if (_dictionary != null)
  178. {
  179. return _dictionary.GetEnumerator();
  180. }
  181. if (_list != null)
  182. {
  183. return _list.GetEnumerator();
  184. }
  185. return System.Linq.Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  186. }
  187. IEnumerator IEnumerable.GetEnumerator()
  188. {
  189. if (_dictionary != null)
  190. {
  191. return _dictionary.GetEnumerator();
  192. }
  193. if (_list != null)
  194. {
  195. return _list.GetEnumerator();
  196. }
  197. return System.Linq.Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  198. }
  199. public bool Remove(Key key)
  200. {
  201. if (_dictionary != null)
  202. {
  203. return _dictionary.Remove(key);
  204. }
  205. return _list != null && _list.Remove(key);
  206. }
  207. /// <summary>
  208. /// Optimization when no need to check for existing items.
  209. /// </summary>
  210. public bool CheckExistingKeys
  211. {
  212. set
  213. {
  214. if (_list != null)
  215. {
  216. _list.CheckExistingKeys = value;
  217. }
  218. }
  219. }
  220. }
  221. }