KPageBitmap.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using Ryujinx.Common;
  2. using System;
  3. using System.Numerics;
  4. namespace Ryujinx.HLE.HOS.Kernel.Memory
  5. {
  6. class KPageBitmap
  7. {
  8. private struct RandomNumberGenerator
  9. {
  10. private uint _entropy;
  11. private uint _bitsAvailable;
  12. private void RefreshEntropy()
  13. {
  14. _entropy = 0;
  15. _bitsAvailable = sizeof(uint) * 8;
  16. }
  17. private bool GenerateRandomBit()
  18. {
  19. if (_bitsAvailable == 0)
  20. {
  21. RefreshEntropy();
  22. }
  23. bool bit = (_entropy & 1) != 0;
  24. _entropy >>= 1;
  25. _bitsAvailable--;
  26. return bit;
  27. }
  28. public int SelectRandomBit(ulong bitmap)
  29. {
  30. int selected = 0;
  31. int bitsCount = UInt64BitSize / 2;
  32. ulong mask = (1UL << bitsCount) - 1;
  33. while (bitsCount != 0)
  34. {
  35. ulong low = bitmap & mask;
  36. ulong high = (bitmap >> bitsCount) & mask;
  37. bool chooseLow;
  38. if (high == 0)
  39. {
  40. chooseLow = true;
  41. }
  42. else if (low == 0)
  43. {
  44. chooseLow = false;
  45. }
  46. else
  47. {
  48. chooseLow = GenerateRandomBit();
  49. }
  50. if (chooseLow)
  51. {
  52. bitmap = low;
  53. }
  54. else
  55. {
  56. bitmap = high;
  57. selected += bitsCount;
  58. }
  59. bitsCount /= 2;
  60. mask >>= bitsCount;
  61. }
  62. return selected;
  63. }
  64. }
  65. private const int UInt64BitSize = sizeof(ulong) * 8;
  66. private const int MaxDepth = 4;
  67. private readonly RandomNumberGenerator _rng;
  68. private readonly ArraySegment<ulong>[] _bitStorages;
  69. private int _usedDepths;
  70. public int BitsCount { get; private set; }
  71. public int HighestDepthIndex => _usedDepths - 1;
  72. public KPageBitmap()
  73. {
  74. _rng = new RandomNumberGenerator();
  75. _bitStorages = new ArraySegment<ulong>[MaxDepth];
  76. }
  77. public ArraySegment<ulong> Initialize(ArraySegment<ulong> storage, ulong size)
  78. {
  79. _usedDepths = GetRequiredDepth(size);
  80. for (int depth = HighestDepthIndex; depth >= 0; depth--)
  81. {
  82. _bitStorages[depth] = storage;
  83. size = BitUtils.DivRoundUp(size, UInt64BitSize);
  84. storage = storage.Slice((int)size);
  85. }
  86. return storage;
  87. }
  88. public ulong FindFreeBlock(bool random)
  89. {
  90. ulong offset = 0;
  91. int depth = 0;
  92. if (random)
  93. {
  94. do
  95. {
  96. ulong v = _bitStorages[depth][(int)offset];
  97. if (v == 0)
  98. {
  99. return ulong.MaxValue;
  100. }
  101. offset = offset * UInt64BitSize + (ulong)_rng.SelectRandomBit(v);
  102. }
  103. while (++depth < _usedDepths);
  104. }
  105. else
  106. {
  107. do
  108. {
  109. ulong v = _bitStorages[depth][(int)offset];
  110. if (v == 0)
  111. {
  112. return ulong.MaxValue;
  113. }
  114. offset = offset * UInt64BitSize + (ulong)BitOperations.TrailingZeroCount(v);
  115. }
  116. while (++depth < _usedDepths);
  117. }
  118. return offset;
  119. }
  120. public void SetBit(ulong offset)
  121. {
  122. SetBit(HighestDepthIndex, offset);
  123. BitsCount++;
  124. }
  125. public void ClearBit(ulong offset)
  126. {
  127. ClearBit(HighestDepthIndex, offset);
  128. BitsCount--;
  129. }
  130. public bool ClearRange(ulong offset, int count)
  131. {
  132. int depth = HighestDepthIndex;
  133. var bits = _bitStorages[depth];
  134. int bitInd = (int)(offset / UInt64BitSize);
  135. if (count < UInt64BitSize)
  136. {
  137. int shift = (int)(offset % UInt64BitSize);
  138. ulong mask = ((1UL << count) - 1) << shift;
  139. ulong v = bits[bitInd];
  140. if ((v & mask) != mask)
  141. {
  142. return false;
  143. }
  144. v &= ~mask;
  145. bits[bitInd] = v;
  146. if (v == 0)
  147. {
  148. ClearBit(depth - 1, (ulong)bitInd);
  149. }
  150. }
  151. else
  152. {
  153. int remaining = count;
  154. int i = 0;
  155. do
  156. {
  157. if (bits[bitInd + i++] != ulong.MaxValue)
  158. {
  159. return false;
  160. }
  161. remaining -= UInt64BitSize;
  162. }
  163. while (remaining > 0);
  164. remaining = count;
  165. i = 0;
  166. do
  167. {
  168. bits[bitInd + i] = 0;
  169. ClearBit(depth - 1, (ulong)(bitInd + i));
  170. i++;
  171. remaining -= UInt64BitSize;
  172. }
  173. while (remaining > 0);
  174. }
  175. BitsCount -= count;
  176. return true;
  177. }
  178. private void SetBit(int depth, ulong offset)
  179. {
  180. while (depth >= 0)
  181. {
  182. int ind = (int)(offset / UInt64BitSize);
  183. int which = (int)(offset % UInt64BitSize);
  184. ulong mask = 1UL << which;
  185. ulong v = _bitStorages[depth][ind];
  186. _bitStorages[depth][ind] = v | mask;
  187. if (v != 0)
  188. {
  189. break;
  190. }
  191. offset = (ulong)ind;
  192. depth--;
  193. }
  194. }
  195. private void ClearBit(int depth, ulong offset)
  196. {
  197. while (depth >= 0)
  198. {
  199. int ind = (int)(offset / UInt64BitSize);
  200. int which = (int)(offset % UInt64BitSize);
  201. ulong mask = 1UL << which;
  202. ulong v = _bitStorages[depth][ind];
  203. v &= ~mask;
  204. _bitStorages[depth][ind] = v;
  205. if (v != 0)
  206. {
  207. break;
  208. }
  209. offset = (ulong)ind;
  210. depth--;
  211. }
  212. }
  213. private static int GetRequiredDepth(ulong regionSize)
  214. {
  215. int depth = 0;
  216. do
  217. {
  218. regionSize /= UInt64BitSize;
  219. depth++;
  220. }
  221. while (regionSize != 0);
  222. return depth;
  223. }
  224. public static int CalculateManagementOverheadSize(ulong regionSize)
  225. {
  226. int overheadBits = 0;
  227. for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
  228. {
  229. regionSize = BitUtils.DivRoundUp(regionSize, UInt64BitSize);
  230. overheadBits += (int)regionSize;
  231. }
  232. return overheadBits * sizeof(ulong);
  233. }
  234. }
  235. }