IntervalTree.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. using Ryujinx.Common.Collections;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Memory.WindowsShared
  5. {
  6. /// <summary>
  7. /// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
  8. /// </summary>
  9. /// <typeparam name="K">Key</typeparam>
  10. /// <typeparam name="V">Value</typeparam>
  11. class IntervalTree<K, V> : IntrusiveRedBlackTreeImpl<IntervalTreeNode<K, V>> where K : IComparable<K>
  12. {
  13. private const int ArrayGrowthSize = 32;
  14. #region Public Methods
  15. /// <summary>
  16. /// Gets the values of the interval whose key is <paramref name="key"/>.
  17. /// </summary>
  18. /// <param name="key">Key of the node value to get</param>
  19. /// <param name="value">Value with the given <paramref name="key"/></param>
  20. /// <returns>True if the key is on the dictionary, false otherwise</returns>
  21. public bool TryGet(K key, out V value)
  22. {
  23. IntervalTreeNode<K, V> node = GetNode(key);
  24. if (node == null)
  25. {
  26. value = default;
  27. return false;
  28. }
  29. value = node.Value;
  30. return true;
  31. }
  32. /// <summary>
  33. /// Returns the start addresses of the intervals whose start and end keys overlap the given range.
  34. /// </summary>
  35. /// <param name="start">Start of the range</param>
  36. /// <param name="end">End of the range</param>
  37. /// <param name="overlaps">Overlaps array to place results in</param>
  38. /// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
  39. /// <returns>Number of intervals found</returns>
  40. public int Get(K start, K end, ref IntervalTreeNode<K, V>[] overlaps, int overlapCount = 0)
  41. {
  42. GetNodes(Root, start, end, ref overlaps, ref overlapCount);
  43. return overlapCount;
  44. }
  45. /// <summary>
  46. /// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
  47. /// </summary>
  48. /// <param name="start">Start of the range to add</param>
  49. /// <param name="end">End of the range to insert</param>
  50. /// <param name="value">Value to add</param>
  51. /// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
  52. public void Add(K start, K end, V value)
  53. {
  54. if (value == null)
  55. {
  56. throw new ArgumentNullException(nameof(value));
  57. }
  58. BSTInsert(start, end, value, null, out _);
  59. }
  60. /// <summary>
  61. /// Removes a value from the tree, searching for it with <paramref name="key"/>.
  62. /// </summary>
  63. /// <param name="key">Key of the node to remove</param>
  64. /// <returns>Number of deleted values</returns>
  65. public int Remove(K key)
  66. {
  67. return Remove(GetNode(key));
  68. }
  69. /// <summary>
  70. /// Removes a value from the tree, searching for it with <paramref name="key"/>.
  71. /// </summary>
  72. /// <param name="nodeToDelete">Node to be removed</param>
  73. /// <returns>Number of deleted values</returns>
  74. public int Remove(IntervalTreeNode<K, V> nodeToDelete)
  75. {
  76. if (nodeToDelete == null)
  77. {
  78. return 0;
  79. }
  80. Delete(nodeToDelete);
  81. Count--;
  82. return 1;
  83. }
  84. /// <summary>
  85. /// Adds all the nodes in the dictionary into <paramref name="list"/>.
  86. /// </summary>
  87. /// <returns>A list of all values sorted by Key Order</returns>
  88. public List<V> AsList()
  89. {
  90. List<V> list = new List<V>();
  91. AddToList(Root, list);
  92. return list;
  93. }
  94. #endregion
  95. #region Private Methods (BST)
  96. /// <summary>
  97. /// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
  98. /// </summary>
  99. /// <param name="node">The node to search for values within</param>
  100. /// <param name="list">The list to add values to</param>
  101. private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
  102. {
  103. if (node == null)
  104. {
  105. return;
  106. }
  107. AddToList(node.Left, list);
  108. list.Add(node.Value);
  109. AddToList(node.Right, list);
  110. }
  111. /// <summary>
  112. /// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
  113. /// </summary>
  114. /// <param name="key">Key of the node to get</param>
  115. /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
  116. /// <returns>Node reference in the tree</returns>
  117. private IntervalTreeNode<K, V> GetNode(K key)
  118. {
  119. if (key == null)
  120. {
  121. throw new ArgumentNullException(nameof(key));
  122. }
  123. IntervalTreeNode<K, V> node = Root;
  124. while (node != null)
  125. {
  126. int cmp = key.CompareTo(node.Start);
  127. if (cmp < 0)
  128. {
  129. node = node.Left;
  130. }
  131. else if (cmp > 0)
  132. {
  133. node = node.Right;
  134. }
  135. else
  136. {
  137. return node;
  138. }
  139. }
  140. return null;
  141. }
  142. /// <summary>
  143. /// Retrieve all nodes that overlap the given start and end keys.
  144. /// </summary>
  145. /// <param name="start">Start of the range</param>
  146. /// <param name="end">End of the range</param>
  147. /// <param name="overlaps">Overlaps array to place results in</param>
  148. /// <param name="overlapCount">Overlaps count to update</param>
  149. private void GetNodes(IntervalTreeNode<K, V> node, K start, K end, ref IntervalTreeNode<K, V>[] overlaps, ref int overlapCount)
  150. {
  151. if (node == null || start.CompareTo(node.Max) >= 0)
  152. {
  153. return;
  154. }
  155. GetNodes(node.Left, start, end, ref overlaps, ref overlapCount);
  156. bool endsOnRight = end.CompareTo(node.Start) > 0;
  157. if (endsOnRight)
  158. {
  159. if (start.CompareTo(node.End) < 0)
  160. {
  161. if (overlaps.Length >= overlapCount)
  162. {
  163. Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
  164. }
  165. overlaps[overlapCount++] = node;
  166. }
  167. GetNodes(node.Right, start, end, ref overlaps, ref overlapCount);
  168. }
  169. }
  170. /// <summary>
  171. /// Propagate an increase in max value starting at the given node, heading up the tree.
  172. /// This should only be called if the max increases - not for rebalancing or removals.
  173. /// </summary>
  174. /// <param name="node">The node to start propagating from</param>
  175. private void PropagateIncrease(IntervalTreeNode<K, V> node)
  176. {
  177. K max = node.Max;
  178. IntervalTreeNode<K, V> ptr = node;
  179. while ((ptr = ptr.Parent) != null)
  180. {
  181. if (max.CompareTo(ptr.Max) > 0)
  182. {
  183. ptr.Max = max;
  184. }
  185. else
  186. {
  187. break;
  188. }
  189. }
  190. }
  191. /// <summary>
  192. /// Propagate recalculating max value starting at the given node, heading up the tree.
  193. /// This fully recalculates the max value from all children when there is potential for it to decrease.
  194. /// </summary>
  195. /// <param name="node">The node to start propagating from</param>
  196. private void PropagateFull(IntervalTreeNode<K, V> node)
  197. {
  198. IntervalTreeNode<K, V> ptr = node;
  199. do
  200. {
  201. K max = ptr.End;
  202. if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
  203. {
  204. max = ptr.Left.Max;
  205. }
  206. if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
  207. {
  208. max = ptr.Right.Max;
  209. }
  210. ptr.Max = max;
  211. } while ((ptr = ptr.Parent) != null);
  212. }
  213. /// <summary>
  214. /// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
  215. /// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
  216. /// Each node can contain multiple values, and has an end address which is the maximum of all those values.
  217. /// Post insertion, the "max" value of the node and all parents are updated.
  218. /// </summary>
  219. /// <param name="start">Start of the range to insert</param>
  220. /// <param name="end">End of the range to insert</param>
  221. /// <param name="value">Value to insert</param>
  222. /// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
  223. /// <param name="outNode">Node that was inserted or modified</param>
  224. /// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
  225. private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
  226. {
  227. IntervalTreeNode<K, V> parent = null;
  228. IntervalTreeNode<K, V> node = Root;
  229. while (node != null)
  230. {
  231. parent = node;
  232. int cmp = start.CompareTo(node.Start);
  233. if (cmp < 0)
  234. {
  235. node = node.Left;
  236. }
  237. else if (cmp > 0)
  238. {
  239. node = node.Right;
  240. }
  241. else
  242. {
  243. outNode = node;
  244. if (updateFactoryCallback != null)
  245. {
  246. // Replace
  247. node.Value = updateFactoryCallback(start, node.Value);
  248. int endCmp = end.CompareTo(node.End);
  249. if (endCmp > 0)
  250. {
  251. node.End = end;
  252. if (end.CompareTo(node.Max) > 0)
  253. {
  254. node.Max = end;
  255. PropagateIncrease(node);
  256. RestoreBalanceAfterInsertion(node);
  257. }
  258. }
  259. else if (endCmp < 0)
  260. {
  261. node.End = end;
  262. PropagateFull(node);
  263. }
  264. }
  265. return false;
  266. }
  267. }
  268. IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
  269. if (newNode.Parent == null)
  270. {
  271. Root = newNode;
  272. }
  273. else if (start.CompareTo(parent.Start) < 0)
  274. {
  275. parent.Left = newNode;
  276. }
  277. else
  278. {
  279. parent.Right = newNode;
  280. }
  281. PropagateIncrease(newNode);
  282. Count++;
  283. RestoreBalanceAfterInsertion(newNode);
  284. outNode = newNode;
  285. return true;
  286. }
  287. /// <summary>
  288. /// Removes the value from the dictionary after searching for it with <paramref name="key">.
  289. /// </summary>
  290. /// <param name="key">Tree node to be removed</param>
  291. private void Delete(IntervalTreeNode<K, V> nodeToDelete)
  292. {
  293. IntervalTreeNode<K, V> replacementNode;
  294. if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
  295. {
  296. replacementNode = nodeToDelete;
  297. }
  298. else
  299. {
  300. replacementNode = nodeToDelete.Predecessor;
  301. }
  302. IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
  303. if (tmp != null)
  304. {
  305. tmp.Parent = ParentOf(replacementNode);
  306. }
  307. if (ParentOf(replacementNode) == null)
  308. {
  309. Root = tmp;
  310. }
  311. else if (replacementNode == LeftOf(ParentOf(replacementNode)))
  312. {
  313. ParentOf(replacementNode).Left = tmp;
  314. }
  315. else
  316. {
  317. ParentOf(replacementNode).Right = tmp;
  318. }
  319. if (replacementNode != nodeToDelete)
  320. {
  321. nodeToDelete.Start = replacementNode.Start;
  322. nodeToDelete.Value = replacementNode.Value;
  323. nodeToDelete.End = replacementNode.End;
  324. nodeToDelete.Max = replacementNode.Max;
  325. }
  326. PropagateFull(replacementNode);
  327. if (tmp != null && ColorOf(replacementNode) == Black)
  328. {
  329. RestoreBalanceAfterRemoval(tmp);
  330. }
  331. }
  332. #endregion
  333. #region Private Methods (RBL)
  334. protected override void RotateLeft(IntervalTreeNode<K, V> node)
  335. {
  336. if (node != null)
  337. {
  338. base.RotateLeft(node);
  339. PropagateFull(node);
  340. }
  341. }
  342. protected override void RotateRight(IntervalTreeNode<K, V> node)
  343. {
  344. if (node != null)
  345. {
  346. base.RotateRight(node);
  347. PropagateFull(node);
  348. }
  349. }
  350. #endregion
  351. public bool ContainsKey(K key)
  352. {
  353. return GetNode(key) != null;
  354. }
  355. }
  356. /// <summary>
  357. /// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
  358. /// </summary>
  359. /// <typeparam name="K">Key type of the node</typeparam>
  360. /// <typeparam name="V">Value type of the node</typeparam>
  361. class IntervalTreeNode<K, V> : IntrusiveRedBlackTreeNode<IntervalTreeNode<K, V>>
  362. {
  363. /// <summary>
  364. /// The start of the range.
  365. /// </summary>
  366. public K Start;
  367. /// <summary>
  368. /// The end of the range.
  369. /// </summary>
  370. public K End;
  371. /// <summary>
  372. /// The maximum end value of this node and all its children.
  373. /// </summary>
  374. public K Max;
  375. /// <summary>
  376. /// Value stored on this node.
  377. /// </summary>
  378. public V Value;
  379. public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
  380. {
  381. Start = start;
  382. End = end;
  383. Max = end;
  384. Value = value;
  385. Parent = parent;
  386. }
  387. }
  388. }