MultiRangeList.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. MemoryRange subrange = range.GetSubRange(i);
  27. if (MemoryRange.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. MemoryRange subrange = range.GetSubRange(i);
  47. if (MemoryRange.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. MemoryRange subrange = range.GetSubRange(i);
  83. if (MemoryRange.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. /// Gets all items on the list starting at the specified memory address.
  120. /// </summary>
  121. /// <param name="baseAddress">Base address to find</param>
  122. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  123. /// <returns>The number of matches found</returns>
  124. public int FindOverlaps(ulong baseAddress, ref T[] output)
  125. {
  126. int count = _items.Get(baseAddress, ref output);
  127. // Only output items with matching base address
  128. int insertPtr = 0;
  129. for (int i = 0; i < count; i++)
  130. {
  131. if (output[i].BaseAddress == baseAddress)
  132. {
  133. if (i != insertPtr)
  134. {
  135. output[insertPtr] = output[i];
  136. }
  137. insertPtr++;
  138. }
  139. }
  140. return insertPtr;
  141. }
  142. private List<T> GetList()
  143. {
  144. List<RangeNode<ulong, T>> items = _items.AsList();
  145. List<T> result = new();
  146. foreach (RangeNode<ulong, T> item in items)
  147. {
  148. if (item.Start == item.Value.BaseAddress)
  149. {
  150. result.Add(item.Value);
  151. }
  152. }
  153. return result;
  154. }
  155. public IEnumerator<T> GetEnumerator()
  156. {
  157. return GetList().GetEnumerator();
  158. }
  159. IEnumerator IEnumerable.GetEnumerator()
  160. {
  161. return GetList().GetEnumerator();
  162. }
  163. }
  164. }