HybridDictionary.cs 5.8 KB

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