HybridDictionary.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. public void SetOrUpdateValue<TState>(Key key, Func<TValue, TState, TValue> updater, TState state)
  69. {
  70. if (_dictionary != null)
  71. {
  72. _dictionary.SetOrUpdateValue(key, updater, state);
  73. }
  74. else if (_list != null)
  75. {
  76. _list.SetOrUpdateValue(key, updater, state);
  77. }
  78. else
  79. {
  80. _list = new ListDictionary<TValue>(key, updater(default, state), _checkExistingKeys);
  81. }
  82. }
  83. private void SwitchToDictionary(Key key, TValue value)
  84. {
  85. var dictionary = new StringDictionarySlim<TValue>(InitialDictionarySize);
  86. foreach (var pair in _list)
  87. {
  88. dictionary[pair.Key] = pair.Value;
  89. }
  90. dictionary[key] = value;
  91. _dictionary = dictionary;
  92. _list = null;
  93. }
  94. public int Count
  95. {
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. get => _dictionary?.Count ?? _list?.Count ?? 0;
  98. }
  99. public void Add(Key key, TValue value)
  100. {
  101. if (_dictionary != null)
  102. {
  103. _dictionary.GetOrAddValueRef(key) = value;
  104. }
  105. else
  106. {
  107. if (_list == null)
  108. {
  109. _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
  110. }
  111. else
  112. {
  113. if (_list.Count + 1 >= CutoverPoint)
  114. {
  115. SwitchToDictionary(key, value);
  116. }
  117. else
  118. {
  119. _list.Add(key, value);
  120. }
  121. }
  122. }
  123. }
  124. public void Clear()
  125. {
  126. _dictionary?.Clear();
  127. _list?.Clear();
  128. }
  129. public bool ContainsKey(Key key)
  130. {
  131. if (_dictionary != null)
  132. {
  133. return _dictionary.ContainsKey(key);
  134. }
  135. if (_list != null)
  136. {
  137. return _list.ContainsKey(key);
  138. }
  139. return false;
  140. }
  141. IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator()
  142. {
  143. if (_dictionary != null)
  144. {
  145. return _dictionary.GetEnumerator();
  146. }
  147. if (_list != null)
  148. {
  149. return _list.GetEnumerator();
  150. }
  151. return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  152. }
  153. IEnumerator IEnumerable.GetEnumerator()
  154. {
  155. if (_dictionary != null)
  156. {
  157. return _dictionary.GetEnumerator();
  158. }
  159. if (_list != null)
  160. {
  161. return _list.GetEnumerator();
  162. }
  163. return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
  164. }
  165. public bool Remove(Key key)
  166. {
  167. if (_dictionary != null)
  168. {
  169. return _dictionary.Remove(key);
  170. }
  171. return _list != null && _list.Remove(key);
  172. }
  173. /// <summary>
  174. /// Optimization when no need to check for existing items.
  175. /// </summary>
  176. public bool CheckExistingKeys
  177. {
  178. set
  179. {
  180. if (_list != null)
  181. {
  182. _list.CheckExistingKeys = value;
  183. }
  184. }
  185. }
  186. }
  187. }