| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- using Ryujinx.Common;
- using System;
- using System.Numerics;
- namespace Ryujinx.HLE.HOS.Kernel.Memory
- {
- class KPageBitmap
- {
- private struct RandomNumberGenerator
- {
- private uint _entropy;
- private uint _bitsAvailable;
- private void RefreshEntropy()
- {
- _entropy = 0;
- _bitsAvailable = sizeof(uint) * 8;
- }
- private bool GenerateRandomBit()
- {
- if (_bitsAvailable == 0)
- {
- RefreshEntropy();
- }
- bool bit = (_entropy & 1) != 0;
- _entropy >>= 1;
- _bitsAvailable--;
- return bit;
- }
- public int SelectRandomBit(ulong bitmap)
- {
- int selected = 0;
- int bitsCount = UInt64BitSize / 2;
- ulong mask = (1UL << bitsCount) - 1;
- while (bitsCount != 0)
- {
- ulong low = bitmap & mask;
- ulong high = (bitmap >> bitsCount) & mask;
- bool chooseLow;
- if (high == 0)
- {
- chooseLow = true;
- }
- else if (low == 0)
- {
- chooseLow = false;
- }
- else
- {
- chooseLow = GenerateRandomBit();
- }
- if (chooseLow)
- {
- bitmap = low;
- }
- else
- {
- bitmap = high;
- selected += bitsCount;
- }
- bitsCount /= 2;
- mask >>= bitsCount;
- }
- return selected;
- }
- }
- private const int UInt64BitSize = sizeof(ulong) * 8;
- private const int MaxDepth = 4;
- private readonly RandomNumberGenerator _rng;
- private readonly ArraySegment<ulong>[] _bitStorages;
- private int _usedDepths;
- public int BitsCount { get; private set; }
- public int HighestDepthIndex => _usedDepths - 1;
- public KPageBitmap()
- {
- _rng = new RandomNumberGenerator();
- _bitStorages = new ArraySegment<ulong>[MaxDepth];
- }
- public ArraySegment<ulong> Initialize(ArraySegment<ulong> storage, ulong size)
- {
- _usedDepths = GetRequiredDepth(size);
- for (int depth = HighestDepthIndex; depth >= 0; depth--)
- {
- _bitStorages[depth] = storage;
- size = BitUtils.DivRoundUp(size, UInt64BitSize);
- storage = storage.Slice((int)size);
- }
- return storage;
- }
- public ulong FindFreeBlock(bool random)
- {
- ulong offset = 0;
- int depth = 0;
- if (random)
- {
- do
- {
- ulong v = _bitStorages[depth][(int)offset];
- if (v == 0)
- {
- return ulong.MaxValue;
- }
- offset = offset * UInt64BitSize + (ulong)_rng.SelectRandomBit(v);
- }
- while (++depth < _usedDepths);
- }
- else
- {
- do
- {
- ulong v = _bitStorages[depth][(int)offset];
- if (v == 0)
- {
- return ulong.MaxValue;
- }
- offset = offset * UInt64BitSize + (ulong)BitOperations.TrailingZeroCount(v);
- }
- while (++depth < _usedDepths);
- }
- return offset;
- }
- public void SetBit(ulong offset)
- {
- SetBit(HighestDepthIndex, offset);
- BitsCount++;
- }
- public void ClearBit(ulong offset)
- {
- ClearBit(HighestDepthIndex, offset);
- BitsCount--;
- }
- public bool ClearRange(ulong offset, int count)
- {
- int depth = HighestDepthIndex;
- var bits = _bitStorages[depth];
- int bitInd = (int)(offset / UInt64BitSize);
- if (count < UInt64BitSize)
- {
- int shift = (int)(offset % UInt64BitSize);
- ulong mask = ((1UL << count) - 1) << shift;
- ulong v = bits[bitInd];
- if ((v & mask) != mask)
- {
- return false;
- }
- v &= ~mask;
- bits[bitInd] = v;
- if (v == 0)
- {
- ClearBit(depth - 1, (ulong)bitInd);
- }
- }
- else
- {
- int remaining = count;
- int i = 0;
- do
- {
- if (bits[bitInd + i++] != ulong.MaxValue)
- {
- return false;
- }
- remaining -= UInt64BitSize;
- }
- while (remaining > 0);
- remaining = count;
- i = 0;
- do
- {
- bits[bitInd + i] = 0;
- ClearBit(depth - 1, (ulong)(bitInd + i));
- i++;
- remaining -= UInt64BitSize;
- }
- while (remaining > 0);
- }
- BitsCount -= count;
- return true;
- }
- private void SetBit(int depth, ulong offset)
- {
- while (depth >= 0)
- {
- int ind = (int)(offset / UInt64BitSize);
- int which = (int)(offset % UInt64BitSize);
- ulong mask = 1UL << which;
- ulong v = _bitStorages[depth][ind];
- _bitStorages[depth][ind] = v | mask;
- if (v != 0)
- {
- break;
- }
- offset = (ulong)ind;
- depth--;
- }
- }
- private void ClearBit(int depth, ulong offset)
- {
- while (depth >= 0)
- {
- int ind = (int)(offset / UInt64BitSize);
- int which = (int)(offset % UInt64BitSize);
- ulong mask = 1UL << which;
- ulong v = _bitStorages[depth][ind];
- v &= ~mask;
- _bitStorages[depth][ind] = v;
- if (v != 0)
- {
- break;
- }
- offset = (ulong)ind;
- depth--;
- }
- }
- private static int GetRequiredDepth(ulong regionSize)
- {
- int depth = 0;
- do
- {
- regionSize /= UInt64BitSize;
- depth++;
- }
- while (regionSize != 0);
- return depth;
- }
- public static int CalculateManagementOverheadSize(ulong regionSize)
- {
- int overheadBits = 0;
- for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
- {
- regionSize = BitUtils.DivRoundUp(regionSize, UInt64BitSize);
- overheadBits += (int)regionSize;
- }
- return overheadBits * sizeof(ulong);
- }
- }
- }
|