RangeList.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Graphics.Gpu.Memory
  5. {
  6. /// <summary>
  7. /// List of GPU resources with data on guest memory.
  8. /// </summary>
  9. /// <typeparam name="T">Type of the GPU resource</typeparam>
  10. class RangeList<T> : IEnumerable<T> where T : IRange
  11. {
  12. private const int ArrayGrowthSize = 32;
  13. private readonly List<T> _items;
  14. /// <summary>
  15. /// Creates a new GPU resources list.
  16. /// </summary>
  17. public RangeList()
  18. {
  19. _items = new List<T>();
  20. }
  21. /// <summary>
  22. /// Adds a new item to the list.
  23. /// </summary>
  24. /// <param name="item">The item to be added</param>
  25. public void Add(T item)
  26. {
  27. int index = BinarySearch(item.Address);
  28. if (index < 0)
  29. {
  30. index = ~index;
  31. }
  32. _items.Insert(index, item);
  33. }
  34. /// <summary>
  35. /// Removes an item from the list.
  36. /// </summary>
  37. /// <param name="item">The item to be removed</param>
  38. /// <returns>True if the item was removed, or false if it was not found</returns>
  39. public bool Remove(T item)
  40. {
  41. int index = BinarySearch(item.Address);
  42. if (index >= 0)
  43. {
  44. while (index > 0 && _items[index - 1].Address == item.Address)
  45. {
  46. index--;
  47. }
  48. while (index < _items.Count)
  49. {
  50. if (_items[index].Equals(item))
  51. {
  52. _items.RemoveAt(index);
  53. return true;
  54. }
  55. if (_items[index].Address > item.Address)
  56. {
  57. break;
  58. }
  59. index++;
  60. }
  61. }
  62. return false;
  63. }
  64. /// <summary>
  65. /// Gets the first item on the list overlapping in memory with the specified item.
  66. /// </summary>
  67. /// <remarks>
  68. /// Despite the name, this has no ordering guarantees of the returned item.
  69. /// It only ensures that the item returned overlaps the specified item.
  70. /// </remarks>
  71. /// <param name="item">Item to check for overlaps</param>
  72. /// <returns>The overlapping item, or the default value for the type if none found</returns>
  73. public T FindFirstOverlap(T item)
  74. {
  75. return FindFirstOverlap(item.Address, item.Size);
  76. }
  77. /// <summary>
  78. /// Gets the first item on the list overlapping the specified memory range.
  79. /// </summary>
  80. /// <remarks>
  81. /// Despite the name, this has no ordering guarantees of the returned item.
  82. /// It only ensures that the item returned overlaps the specified memory range.
  83. /// </remarks>
  84. /// <param name="address">Start address of the range</param>
  85. /// <param name="size">Size in bytes of the range</param>
  86. /// <returns>The overlapping item, or the default value for the type if none found</returns>
  87. public T FindFirstOverlap(ulong address, ulong size)
  88. {
  89. int index = BinarySearch(address, size);
  90. if (index < 0)
  91. {
  92. return default(T);
  93. }
  94. return _items[index];
  95. }
  96. /// <summary>
  97. /// Gets all items overlapping with the specified item in memory.
  98. /// </summary>
  99. /// <param name="item">Item to check for overlaps</param>
  100. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  101. /// <returns>The number of overlapping items found</returns>
  102. public int FindOverlaps(T item, ref T[] output)
  103. {
  104. return FindOverlaps(item.Address, item.Size, ref output);
  105. }
  106. /// <summary>
  107. /// Gets all items on the list overlapping the specified memory range.
  108. /// </summary>
  109. /// <param name="address">Start address of the range</param>
  110. /// <param name="size">Size in bytes of the range</param>
  111. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  112. /// <returns>The number of overlapping items found</returns>
  113. public int FindOverlaps(ulong address, ulong size, ref T[] output)
  114. {
  115. int outputIndex = 0;
  116. ulong endAddress = address + size;
  117. lock (_items)
  118. {
  119. foreach (T item in _items)
  120. {
  121. if (item.Address >= endAddress)
  122. {
  123. break;
  124. }
  125. if (item.OverlapsWith(address, size))
  126. {
  127. if (outputIndex == output.Length)
  128. {
  129. Array.Resize(ref output, outputIndex + ArrayGrowthSize);
  130. }
  131. output[outputIndex++] = item;
  132. }
  133. }
  134. }
  135. return outputIndex;
  136. }
  137. /// <summary>
  138. /// Gets all items overlapping with the specified item in memory.
  139. /// </summary>
  140. /// <remarks>
  141. /// This method only returns correct results if none of the items on the list overlaps with
  142. /// each other. If that is not the case, this method should not be used.
  143. /// This method is faster than the regular method to find all overlaps.
  144. /// </remarks>
  145. /// <param name="item">Item to check for overlaps</param>
  146. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  147. /// <returns>The number of overlapping items found</returns>
  148. public int FindOverlapsNonOverlapping(T item, ref T[] output)
  149. {
  150. return FindOverlapsNonOverlapping(item.Address, item.Size, ref output);
  151. }
  152. /// <summary>
  153. /// Gets all items on the list overlapping the specified memory range.
  154. /// </summary>
  155. /// <remarks>
  156. /// This method only returns correct results if none of the items on the list overlaps with
  157. /// each other. If that is not the case, this method should not be used.
  158. /// This method is faster than the regular method to find all overlaps.
  159. /// </remarks>
  160. /// <param name="address">Start address of the range</param>
  161. /// <param name="size">Size in bytes of the range</param>
  162. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  163. /// <returns>The number of overlapping items found</returns>
  164. public int FindOverlapsNonOverlapping(ulong address, ulong size, ref T[] output)
  165. {
  166. // This is a bit faster than FindOverlaps, but only works
  167. // when none of the items on the list overlaps with each other.
  168. int outputIndex = 0;
  169. int index = BinarySearch(address, size);
  170. if (index >= 0)
  171. {
  172. while (index > 0 && _items[index - 1].OverlapsWith(address, size))
  173. {
  174. index--;
  175. }
  176. do
  177. {
  178. if (outputIndex == output.Length)
  179. {
  180. Array.Resize(ref output, outputIndex + ArrayGrowthSize);
  181. }
  182. output[outputIndex++] = _items[index++];
  183. }
  184. while (index < _items.Count && _items[index].OverlapsWith(address, size));
  185. }
  186. return outputIndex;
  187. }
  188. /// <summary>
  189. /// Gets all items on the list with the specified memory address.
  190. /// </summary>
  191. /// <param name="address">Address to find</param>
  192. /// <param name="output">Output array where matches will be written. It is automatically resized to fit the results</param>
  193. /// <returns>The number of matches found</returns>
  194. public int FindOverlaps(ulong address, ref T[] output)
  195. {
  196. int index = BinarySearch(address);
  197. int outputIndex = 0;
  198. if (index >= 0)
  199. {
  200. while (index > 0 && _items[index - 1].Address == address)
  201. {
  202. index--;
  203. }
  204. while (index < _items.Count)
  205. {
  206. T overlap = _items[index++];
  207. if (overlap.Address != address)
  208. {
  209. break;
  210. }
  211. if (outputIndex == output.Length)
  212. {
  213. Array.Resize(ref output, outputIndex + ArrayGrowthSize);
  214. }
  215. output[outputIndex++] = overlap;
  216. }
  217. }
  218. return outputIndex;
  219. }
  220. /// <summary>
  221. /// Performs binary search on the internal list of items.
  222. /// </summary>
  223. /// <param name="address">Address to find</param>
  224. /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns>
  225. private int BinarySearch(ulong address)
  226. {
  227. int left = 0;
  228. int right = _items.Count - 1;
  229. while (left <= right)
  230. {
  231. int range = right - left;
  232. int middle = left + (range >> 1);
  233. T item = _items[middle];
  234. if (item.Address == address)
  235. {
  236. return middle;
  237. }
  238. if (address < item.Address)
  239. {
  240. right = middle - 1;
  241. }
  242. else
  243. {
  244. left = middle + 1;
  245. }
  246. }
  247. return ~left;
  248. }
  249. /// <summary>
  250. /// Performs binary search for items overlapping a given memory range.
  251. /// </summary>
  252. /// <param name="address">Start address of the range</param>
  253. /// <param name="size">Size in bytes of the range</param>
  254. /// <returns>List index of the item, or complement index of nearest item with lower value on the list</returns>
  255. private int BinarySearch(ulong address, ulong size)
  256. {
  257. int left = 0;
  258. int right = _items.Count - 1;
  259. while (left <= right)
  260. {
  261. int range = right - left;
  262. int middle = left + (range >> 1);
  263. T item = _items[middle];
  264. if (item.OverlapsWith(address, size))
  265. {
  266. return middle;
  267. }
  268. if (address < item.Address)
  269. {
  270. right = middle - 1;
  271. }
  272. else
  273. {
  274. left = middle + 1;
  275. }
  276. }
  277. return ~left;
  278. }
  279. public IEnumerator<T> GetEnumerator()
  280. {
  281. return _items.GetEnumerator();
  282. }
  283. IEnumerator IEnumerable.GetEnumerator()
  284. {
  285. return _items.GetEnumerator();
  286. }
  287. }
  288. }