MultiRange.cs 9.7 KB

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