aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs')
-rw-r--r--src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs
new file mode 100644
index 00000000..54c3f8b0
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/SbvSelect.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Numerics;
+
+namespace Ryujinx.Horizon.Sdk.Ngc.Detail
+{
+ class SbvSelect
+ {
+ private uint[] _array;
+ private BitVector32 _bv1;
+ private BitVector32 _bv2;
+ private SbvRank _sbvRank1;
+ private SbvRank _sbvRank2;
+
+ public bool Import(ref BinaryReader reader)
+ {
+ if (!reader.Read(out int arrayLength) ||
+ reader.AllocateAndReadArray(ref _array, arrayLength) != arrayLength)
+ {
+ return false;
+ }
+
+ _bv1 = new();
+ _bv2 = new();
+ _sbvRank1 = new();
+ _sbvRank2 = new();
+
+ return _bv1.Import(ref reader) &&
+ _bv2.Import(ref reader) &&
+ _sbvRank1.Import(ref reader, _bv1.BitLength) &&
+ _sbvRank2.Import(ref reader, _bv2.BitLength);
+ }
+
+ public void Build(ReadOnlySpan<uint> bitmap, int length)
+ {
+ int lengthInWords = (length + Set.BitsPerWord - 1) / Set.BitsPerWord;
+
+ int rank0Length = 0;
+ int rank1Length = 0;
+
+ if (lengthInWords != 0)
+ {
+ for (int index = 0; index < bitmap.Length; index++)
+ {
+ uint value = bitmap[index];
+
+ if (value != 0)
+ {
+ rank0Length++;
+ rank1Length += BitOperations.PopCount(value);
+ }
+ }
+ }
+
+ _bv1 = new(rank0Length);
+ _bv2 = new(rank1Length);
+ _array = new uint[rank0Length];
+
+ bool setSequence = false;
+ int arrayIndex = 0;
+ uint unsetCount = 0;
+ rank0Length = 0;
+ rank1Length = 0;
+
+ if (lengthInWords != 0)
+ {
+ for (int index = 0; index < bitmap.Length; index++)
+ {
+ uint value = bitmap[index];
+
+ if (value != 0)
+ {
+ if (!setSequence)
+ {
+ _bv1.TurnOn(rank0Length);
+ _array[arrayIndex++] = unsetCount;
+ setSequence = true;
+ }
+
+ _bv2.TurnOn(rank1Length);
+
+ rank0Length++;
+ rank1Length += BitOperations.PopCount(value);
+ }
+ else
+ {
+ unsetCount++;
+ setSequence = false;
+ }
+ }
+ }
+
+ _sbvRank1 = new(_bv1.Array, _bv1.BitLength);
+ _sbvRank2 = new(_bv2.Array, _bv2.BitLength);
+ }
+
+ public int Select(Set set, int index)
+ {
+ if (index < _bv2.BitLength)
+ {
+ int rank1PlainIndex = _sbvRank2.CalcRank1(index, _bv2.Array);
+ int rank0PlainIndex = _sbvRank1.CalcRank1(rank1PlainIndex - 1, _bv1.Array);
+
+ int value = (int)_array[rank0PlainIndex - 1] + (rank1PlainIndex - 1);
+
+ int baseBitIndex = 0;
+
+ if (value != 0)
+ {
+ baseBitIndex = value * 32;
+
+ int setBvLength = set.BitVector.BitLength;
+ int bitIndexBounded = baseBitIndex - 1;
+
+ if (bitIndexBounded >= setBvLength)
+ {
+ bitIndexBounded = setBvLength - 1;
+ }
+
+ index -= set.SbvRank.CalcRank1(bitIndexBounded, set.BitVector.Array);
+ }
+
+ return SelectPos(set.BitVector.Array[value], index) + baseBitIndex;
+ }
+
+ return -1;
+ }
+
+ public static int SelectPos(uint membershipBits, int bitIndex)
+ {
+ // Skips "bitIndex" set bits, and returns the bit index of the next set bit.
+ // If there is no set bit after skipping the specified amount, returns 32.
+
+ int bit;
+ int bitCount = bitIndex;
+
+ for (bit = 0; bit < sizeof(uint) * 8;)
+ {
+ if (((membershipBits >> bit) & 1) != 0)
+ {
+ if (bitCount-- == 0)
+ {
+ break;
+ }
+
+ bit++;
+ }
+ else
+ {
+ bit += BitOperations.TrailingZeroCount(membershipBits >> bit);
+ }
+ }
+
+ return bit;
+ }
+ }
+}