HybridDictionary.cs 5.8 KB

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