HybridDictionary.cs 5.5 KB

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