MultiRangeList.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using Ryujinx.Common.Collections;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Memory.Range
  5. {
  6. public class MultiRangeList<T> : IEnumerable<T> where T : IMultiRangeItem
  7. {
  8. private readonly IntervalTree<ulong, T> _items;
  9. public int Count { get; private set; }
  10. /// <summary>
  11. /// Creates a new range list.
  12. /// </summary>
  13. public MultiRangeList()
  14. {
  15. _items = new IntervalTree<ulong, T>();
  16. }
  17. /// <summary>
  18. /// Adds a new item to the list.
  19. /// </summary>
  20. /// <param name="item">The item to be added</param>
  21. public void Add(T item)
  22. {
  23. MultiRange range = item.Range;
  24. for (int i = 0; i < range.Count; i++)
  25. {
  26. var subrange = range.GetSubRange(i);
  27. if (IsInvalid(ref subrange))
  28. {
  29. continue;
  30. }
  31. _items.Add(subrange.Address, subrange.EndAddress, item);
  32. }
  33. Count++;
  34. }
  35. /// <summary>
  36. /// Removes an item from the list.
  37. /// </summary>
  38. /// <param name="item">The item to be removed</param>
  39. /// <returns>True if the item was removed, or false if it was not found</returns>
  40. public bool Remove(T item)
  41. {
  42. MultiRange range = item.Range;
  43. int removed = 0;
  44. for (int i = 0; i < range.Count; i++)
  45. {
  46. var subrange = range.GetSubRange(i);
  47. if (IsInvalid(ref subrange))
  48. {
  49. continue;
  50. }
  51. removed += _items.Remove(subrange.Address, item);
  52. }
  53. if (removed > 0)
  54. {
  55. // All deleted intervals are for the same item - the one we removed.
  56. Count--;
  57. }
  58. return removed > 0;
  59. }
  60. /// <summary>
  61. /// Gets all items on the list overlapping the specified memory range.
  62. /// </summary>
  63. /// <param name="address">Start address of the range</param>
  64. /// <param name="size">Size in bytes of the range</param>
  65. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  66. /// <returns>The number of overlapping items found</returns>
  67. public int FindOverlaps(ulong address, ulong size, ref T[] output)
  68. {
  69. return FindOverlaps(new MultiRange(address, size), ref output);
  70. }
  71. /// <summary>
  72. /// Gets all items on the list overlapping the specified memory ranges.
  73. /// </summary>
  74. /// <param name="range">Ranges of memory being searched</param>
  75. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  76. /// <returns>The number of overlapping items found</returns>
  77. public int FindOverlaps(MultiRange range, ref T[] output)
  78. {
  79. int overlapCount = 0;
  80. for (int i = 0; i < range.Count; i++)
  81. {
  82. var subrange = range.GetSubRange(i);
  83. if (IsInvalid(ref subrange))
  84. {
  85. continue;
  86. }
  87. overlapCount = _items.Get(subrange.Address, subrange.EndAddress, ref output, overlapCount);
  88. }
  89. // Remove any duplicates, caused by items having multiple sub range nodes in the tree.
  90. if (overlapCount > 1)
  91. {
  92. int insertPtr = 0;
  93. for (int i = 0; i < overlapCount; i++)
  94. {
  95. T item = output[i];
  96. bool duplicate = false;
  97. for (int j = insertPtr - 1; j >= 0; j--)
  98. {
  99. if (item.Equals(output[j]))
  100. {
  101. duplicate = true;
  102. break;
  103. }
  104. }
  105. if (!duplicate)
  106. {
  107. if (insertPtr != i)
  108. {
  109. output[insertPtr] = item;
  110. }
  111. insertPtr++;
  112. }
  113. }
  114. overlapCount = insertPtr;
  115. }
  116. return overlapCount;
  117. }
  118. /// <summary>
  119. /// Checks if a given sub-range of memory is invalid.
  120. /// Those are used to represent unmapped memory regions (holes in the region mapping).
  121. /// </summary>
  122. /// <param name="subRange">Memory range to checl</param>
  123. /// <returns>True if the memory range is considered invalid, false otherwise</returns>
  124. private static bool IsInvalid(ref MemoryRange subRange)
  125. {
  126. return subRange.Address == ulong.MaxValue;
  127. }
  128. /// <summary>
  129. /// Gets all items on the list starting at the specified memory address.
  130. /// </summary>
  131. /// <param name="baseAddress">Base address to find</param>
  132. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  133. /// <returns>The number of matches found</returns>
  134. public int FindOverlaps(ulong baseAddress, ref T[] output)
  135. {
  136. int count = _items.Get(baseAddress, ref output);
  137. // Only output items with matching base address
  138. int insertPtr = 0;
  139. for (int i = 0; i < count; i++)
  140. {
  141. if (output[i].BaseAddress == baseAddress)
  142. {
  143. if (i != insertPtr)
  144. {
  145. output[insertPtr] = output[i];
  146. }
  147. insertPtr++;
  148. }
  149. }
  150. return insertPtr;
  151. }
  152. private List<T> GetList()
  153. {
  154. var items = _items.AsList();
  155. var result = new List<T>();
  156. foreach (RangeNode<ulong, T> item in items)
  157. {
  158. if (item.Start == item.Value.BaseAddress)
  159. {
  160. result.Add(item.Value);
  161. }
  162. }
  163. return result;
  164. }
  165. public IEnumerator<T> GetEnumerator()
  166. {
  167. return GetList().GetEnumerator();
  168. }
  169. IEnumerator IEnumerable.GetEnumerator()
  170. {
  171. return GetList().GetEnumerator();
  172. }
  173. }
  174. }