MultiRange.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using System;
  2. using System.Collections.Generic;
  3. namespace Ryujinx.Memory.Range
  4. {
  5. /// <summary>
  6. /// Sequence of physical memory regions that a single non-contiguous virtual memory region maps to.
  7. /// </summary>
  8. public readonly struct MultiRange : IEquatable<MultiRange>
  9. {
  10. private const ulong InvalidAddress = ulong.MaxValue;
  11. private readonly MemoryRange _singleRange;
  12. private readonly MemoryRange[] _ranges;
  13. private bool HasSingleRange => _ranges == null;
  14. /// <summary>
  15. /// Total of physical sub-ranges on the virtual memory region.
  16. /// </summary>
  17. public int Count => HasSingleRange ? 1 : _ranges.Length;
  18. /// <summary>
  19. /// Minimum start address of all sub-ranges.
  20. /// </summary>
  21. public ulong MinAddress { get; }
  22. /// <summary>
  23. /// Maximum end address of all sub-ranges.
  24. /// </summary>
  25. public ulong MaxAddress { get; }
  26. /// <summary>
  27. /// Creates a new multi-range with a single physical region.
  28. /// </summary>
  29. /// <param name="address">Start address of the region</param>
  30. /// <param name="size">Size of the region in bytes</param>
  31. public MultiRange(ulong address, ulong size)
  32. {
  33. _singleRange = new MemoryRange(address, size);
  34. _ranges = null;
  35. MinAddress = address;
  36. MaxAddress = address + size;
  37. }
  38. /// <summary>
  39. /// Creates a new multi-range with multiple physical regions.
  40. /// </summary>
  41. /// <param name="ranges">Array of physical regions</param>
  42. /// <exception cref="ArgumentNullException"><paramref name="ranges"/> is null</exception>
  43. public MultiRange(MemoryRange[] ranges)
  44. {
  45. _singleRange = MemoryRange.Empty;
  46. _ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));
  47. if (ranges.Length != 0)
  48. {
  49. MinAddress = ulong.MaxValue;
  50. MaxAddress = 0UL;
  51. foreach (MemoryRange range in ranges)
  52. {
  53. if (MinAddress > range.Address)
  54. {
  55. MinAddress = range.Address;
  56. }
  57. if (MaxAddress < range.EndAddress)
  58. {
  59. MaxAddress = range.EndAddress;
  60. }
  61. }
  62. }
  63. else
  64. {
  65. MinAddress = 0UL;
  66. MaxAddress = 0UL;
  67. }
  68. }
  69. /// <summary>
  70. /// Gets a slice of the multi-range.
  71. /// </summary>
  72. /// <param name="offset">Offset of the slice into the multi-range in bytes</param>
  73. /// <param name="size">Size of the slice in bytes</param>
  74. /// <returns>A new multi-range representing the given slice of this one</returns>
  75. public MultiRange GetSlice(ulong offset, ulong size)
  76. {
  77. if (HasSingleRange)
  78. {
  79. if (_singleRange.Size - offset < size)
  80. {
  81. throw new ArgumentOutOfRangeException(nameof(size));
  82. }
  83. return new MultiRange(_singleRange.Address + offset, size);
  84. }
  85. else
  86. {
  87. var ranges = new List<MemoryRange>();
  88. foreach (MemoryRange range in _ranges)
  89. {
  90. if ((long)offset <= 0)
  91. {
  92. ranges.Add(new MemoryRange(range.Address, Math.Min(size, range.Size)));
  93. size -= range.Size;
  94. }
  95. else if (offset < range.Size)
  96. {
  97. ulong sliceSize = Math.Min(size, range.Size - offset);
  98. if (range.Address == InvalidAddress)
  99. {
  100. ranges.Add(new MemoryRange(range.Address, sliceSize));
  101. }
  102. else
  103. {
  104. ranges.Add(new MemoryRange(range.Address + offset, sliceSize));
  105. }
  106. size -= sliceSize;
  107. }
  108. if ((long)size <= 0)
  109. {
  110. break;
  111. }
  112. offset -= range.Size;
  113. }
  114. return new MultiRange(ranges.ToArray());
  115. }
  116. }
  117. /// <summary>
  118. /// Gets the physical region at the specified index.
  119. /// </summary>
  120. /// <param name="index">Index of the physical region</param>
  121. /// <returns>Region at the index specified</returns>
  122. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is invalid</exception>
  123. public MemoryRange GetSubRange(int index)
  124. {
  125. if (HasSingleRange)
  126. {
  127. if (index != 0)
  128. {
  129. throw new ArgumentOutOfRangeException(nameof(index));
  130. }
  131. return _singleRange;
  132. }
  133. else
  134. {
  135. if ((uint)index >= _ranges.Length)
  136. {
  137. throw new ArgumentOutOfRangeException(nameof(index));
  138. }
  139. return _ranges[index];
  140. }
  141. }
  142. /// <summary>
  143. /// Gets the physical region at the specified index, without explicit bounds checking.
  144. /// </summary>
  145. /// <param name="index">Index of the physical region</param>
  146. /// <returns>Region at the index specified</returns>
  147. private MemoryRange GetSubRangeUnchecked(int index)
  148. {
  149. return HasSingleRange ? _singleRange : _ranges[index];
  150. }
  151. /// <summary>
  152. /// Check if two multi-ranges overlap with each other.
  153. /// </summary>
  154. /// <param name="other">Other multi-range to check for overlap</param>
  155. /// <returns>True if any sub-range overlaps, false otherwise</returns>
  156. public bool OverlapsWith(MultiRange other)
  157. {
  158. if (HasSingleRange && other.HasSingleRange)
  159. {
  160. return _singleRange.OverlapsWith(other._singleRange);
  161. }
  162. else
  163. {
  164. for (int i = 0; i < Count; i++)
  165. {
  166. MemoryRange currentRange = GetSubRangeUnchecked(i);
  167. for (int j = 0; j < other.Count; j++)
  168. {
  169. if (currentRange.OverlapsWith(other.GetSubRangeUnchecked(j)))
  170. {
  171. return true;
  172. }
  173. }
  174. }
  175. }
  176. return false;
  177. }
  178. /// <summary>
  179. /// Checks if a given multi-range is fully contained inside another.
  180. /// </summary>
  181. /// <param name="other">Multi-range to be checked</param>
  182. /// <returns>True if all the sub-ranges on <paramref name="other"/> are contained inside the multi-range, with the same order, false otherwise</returns>
  183. public bool Contains(MultiRange other)
  184. {
  185. return FindOffset(other) >= 0;
  186. }
  187. /// <summary>
  188. /// Calculates the offset of a given multi-range inside another, when the multi-range is fully contained
  189. /// inside the other multi-range, otherwise returns -1.
  190. /// </summary>
  191. /// <param name="other">Multi-range that should be fully contained inside this one</param>
  192. /// <returns>Offset in bytes if fully contained, otherwise -1</returns>
  193. public int FindOffset(MultiRange other)
  194. {
  195. int thisCount = Count;
  196. int otherCount = other.Count;
  197. if (thisCount == 1 && otherCount == 1)
  198. {
  199. MemoryRange otherFirstRange = other.GetSubRangeUnchecked(0);
  200. MemoryRange currentFirstRange = GetSubRangeUnchecked(0);
  201. if (otherFirstRange.Address >= currentFirstRange.Address &&
  202. otherFirstRange.EndAddress <= currentFirstRange.EndAddress)
  203. {
  204. return (int)(otherFirstRange.Address - currentFirstRange.Address);
  205. }
  206. }
  207. else if (thisCount >= otherCount)
  208. {
  209. ulong baseOffset = 0;
  210. MemoryRange otherFirstRange = other.GetSubRangeUnchecked(0);
  211. MemoryRange otherLastRange = other.GetSubRangeUnchecked(otherCount - 1);
  212. for (int i = 0; i < (thisCount - otherCount) + 1; baseOffset += GetSubRangeUnchecked(i).Size, i++)
  213. {
  214. MemoryRange currentFirstRange = GetSubRangeUnchecked(i);
  215. MemoryRange currentLastRange = GetSubRangeUnchecked(i + otherCount - 1);
  216. if (otherCount > 1)
  217. {
  218. if (otherFirstRange.Address < currentFirstRange.Address ||
  219. otherFirstRange.EndAddress != currentFirstRange.EndAddress)
  220. {
  221. continue;
  222. }
  223. if (otherLastRange.Address != currentLastRange.Address ||
  224. otherLastRange.EndAddress > currentLastRange.EndAddress)
  225. {
  226. continue;
  227. }
  228. bool fullMatch = true;
  229. for (int j = 1; j < otherCount - 1; j++)
  230. {
  231. if (!GetSubRangeUnchecked(i + j).Equals(other.GetSubRangeUnchecked(j)))
  232. {
  233. fullMatch = false;
  234. break;
  235. }
  236. }
  237. if (!fullMatch)
  238. {
  239. continue;
  240. }
  241. }
  242. else if (currentFirstRange.Address > otherFirstRange.Address ||
  243. currentFirstRange.EndAddress < otherFirstRange.EndAddress)
  244. {
  245. continue;
  246. }
  247. return (int)(baseOffset + (otherFirstRange.Address - currentFirstRange.Address));
  248. }
  249. }
  250. return -1;
  251. }
  252. /// <summary>
  253. /// Gets the total size of all sub-ranges in bytes.
  254. /// </summary>
  255. /// <returns>Total size in bytes</returns>
  256. public ulong GetSize()
  257. {
  258. if (HasSingleRange)
  259. {
  260. return _singleRange.Size;
  261. }
  262. ulong sum = 0;
  263. foreach (MemoryRange range in _ranges)
  264. {
  265. sum += range.Size;
  266. }
  267. return sum;
  268. }
  269. public override bool Equals(object obj)
  270. {
  271. return obj is MultiRange other && Equals(other);
  272. }
  273. public bool Equals(MultiRange other)
  274. {
  275. if (HasSingleRange && other.HasSingleRange)
  276. {
  277. return _singleRange.Equals(other._singleRange);
  278. }
  279. int thisCount = Count;
  280. if (thisCount != other.Count)
  281. {
  282. return false;
  283. }
  284. for (int i = 0; i < thisCount; i++)
  285. {
  286. if (!GetSubRangeUnchecked(i).Equals(other.GetSubRangeUnchecked(i)))
  287. {
  288. return false;
  289. }
  290. }
  291. return true;
  292. }
  293. public override int GetHashCode()
  294. {
  295. if (HasSingleRange)
  296. {
  297. return _singleRange.GetHashCode();
  298. }
  299. HashCode hash = new HashCode();
  300. foreach (MemoryRange range in _ranges)
  301. {
  302. hash.Add(range);
  303. }
  304. return hash.ToHashCode();
  305. }
  306. }
  307. }