using System; using System.Buffers.Binary; using Ryujinx.Common.Memory; namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { internal struct Reader { private static readonly byte[] Norm = new byte[] { 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private const int BdValueSize = sizeof(ulong) * 8; // This is meant to be a large, positive constant that can still be efficiently // loaded as an immediate (on platforms like ARM, for example). // Even relatively modest values like 100 would work fine. private const int LotsOfBits = 0x40000000; public ulong Value; public uint Range; public int Count; private ArrayPtr _buffer; public bool Init(ArrayPtr buffer, int size) { if (size != 0 && buffer.IsNull) { return true; } else { _buffer = new ArrayPtr(ref buffer[0], size); Value = 0; Count = -8; Range = 255; Fill(); return ReadBit() != 0; // Marker bit } } private void Fill() { ReadOnlySpan buffer = _buffer.ToSpan(); ReadOnlySpan bufferStart = buffer; ulong value = Value; int count = Count; ulong bytesLeft = (ulong)buffer.Length; ulong bitsLeft = bytesLeft * 8; int shift = BdValueSize - 8 - (count + 8); if (bitsLeft > BdValueSize) { int bits = (shift & unchecked((int)0xfffffff8)) + 8; ulong nv; ulong bigEndianValues = BinaryPrimitives.ReadUInt64BigEndian(buffer); nv = bigEndianValues >> (BdValueSize - bits); count += bits; buffer = buffer.Slice(bits >> 3); value = Value | (nv << (shift & 0x7)); } else { int bitsOver = shift + 8 - (int)bitsLeft; int loopEnd = 0; if (bitsOver >= 0) { count += LotsOfBits; loopEnd = bitsOver; } if (bitsOver < 0 || bitsLeft != 0) { while (shift >= loopEnd) { count += 8; value |= (ulong)buffer[0] << shift; buffer = buffer.Slice(1); shift -= 8; } } } // NOTE: Variable 'buffer' may not relate to '_buffer' after decryption, // so we increase '_buffer' by the amount that 'buffer' moved, rather than // assign 'buffer' to '_buffer'. _buffer = _buffer.Slice(bufferStart.Length - buffer.Length); Value = value; Count = count; } public bool HasError() { // Check if we have reached the end of the buffer. // // Variable 'count' stores the number of bits in the 'value' buffer, minus // 8. The top byte is part of the algorithm, and the remainder is buffered // to be shifted into it. So if count == 8, the top 16 bits of 'value' are // occupied, 8 for the algorithm and 8 in the buffer. // // When reading a byte from the user's buffer, count is filled with 8 and // one byte is filled into the value buffer. When we reach the end of the // data, count is additionally filled with LotsOfBits. So when // count == LotsOfBits - 1, the user's data has been exhausted. // // 1 if we have tried to decode bits after the end of stream was encountered. // 0 No error. return Count > BdValueSize && Count < LotsOfBits; } public int Read(int prob) { uint bit = 0; ulong value; ulong bigsplit; int count; uint range; uint split = (Range * (uint)prob + (256 - (uint)prob)) >> 8; if (Count < 0) { Fill(); } value = Value; count = Count; bigsplit = (ulong)split << (BdValueSize - 8); range = split; if (value >= bigsplit) { range = Range - split; value -= bigsplit; bit = 1; } { int shift = Norm[range]; range <<= shift; value <<= shift; count -= shift; } Value = value; Count = count; Range = range; return (int)bit; } public int ReadBit() { return Read(128); // vpx_prob_half } public int ReadLiteral(int bits) { int literal = 0, bit; for (bit = bits - 1; bit >= 0; bit--) { literal |= ReadBit() << bit; } return literal; } public int ReadTree(ReadOnlySpan tree, ReadOnlySpan probs) { sbyte i = 0; while ((i = tree[i + Read(probs[i >> 1])]) > 0) { continue; } return -i; } public int ReadBool(int prob, ref ulong value, ref int count, ref uint range) { uint split = (range * (uint)prob + (256 - (uint)prob)) >> 8; ulong bigsplit = (ulong)split << (BdValueSize - 8); if (count < 0) { Value = value; Count = count; Fill(); value = Value; count = Count; } if (value >= bigsplit) { range = range - split; value = value - bigsplit; { int shift = Norm[range]; range <<= shift; value <<= shift; count -= shift; } return 1; } range = split; { int shift = Norm[range]; range <<= shift; value <<= shift; count -= shift; } return 0; } public ArrayPtr FindEnd() { // Find the end of the coded buffer while (Count > 8 && Count < BdValueSize) { Count -= 8; _buffer = _buffer.Slice(-1); } return _buffer; } } }