Reader.cs
1 using Ryujinx.Common.Memory; 2 using System; 3 using System.Buffers.Binary; 4 5 namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp 6 { 7 internal struct Reader 8 { 9 private static readonly byte[] _norm = { 10 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, 11 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, 12 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, 13 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, 14 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, 15 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, 16 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, 17 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, 18 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, 19 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 }; 21 private const int BdValueSize = sizeof(ulong) * 8; 22 23 // This is meant to be a large, positive constant that can still be efficiently 24 // loaded as an immediate (on platforms like ARM, for example). 25 // Even relatively modest values like 100 would work fine. 26 private const int LotsOfBits = 0x40000000; 27 28 public ulong Value; 29 public uint Range; 30 public int Count; 31 private ArrayPtr<byte> _buffer; 32 33 public bool Init(ArrayPtr<byte> buffer, int size) 34 { 35 if (size != 0 && buffer.IsNull) 36 { 37 return true; 38 } 39 else 40 { 41 _buffer = new ArrayPtr<byte>(ref buffer[0], size); 42 Value = 0; 43 Count = -8; 44 Range = 255; 45 Fill(); 46 47 return ReadBit() != 0; // Marker bit 48 } 49 } 50 51 private void Fill() 52 { 53 ReadOnlySpan<byte> buffer = _buffer.AsSpan(); 54 ReadOnlySpan<byte> bufferStart = buffer; 55 ulong value = Value; 56 int count = Count; 57 ulong bytesLeft = (ulong)buffer.Length; 58 ulong bitsLeft = bytesLeft * 8; 59 int shift = BdValueSize - 8 - (count + 8); 60 61 if (bitsLeft > BdValueSize) 62 { 63 int bits = (shift & unchecked((int)0xfffffff8)) + 8; 64 ulong nv; 65 ulong bigEndianValues = BinaryPrimitives.ReadUInt64BigEndian(buffer); 66 nv = bigEndianValues >> (BdValueSize - bits); 67 count += bits; 68 buffer = buffer[(bits >> 3)..]; 69 value = Value | (nv << (shift & 0x7)); 70 } 71 else 72 { 73 int bitsOver = shift + 8 - (int)bitsLeft; 74 int loopEnd = 0; 75 if (bitsOver >= 0) 76 { 77 count += LotsOfBits; 78 loopEnd = bitsOver; 79 } 80 81 if (bitsOver < 0 || bitsLeft != 0) 82 { 83 while (shift >= loopEnd) 84 { 85 count += 8; 86 value |= (ulong)buffer[0] << shift; 87 buffer = buffer[1..]; 88 shift -= 8; 89 } 90 } 91 } 92 93 // NOTE: Variable 'buffer' may not relate to '_buffer' after decryption, 94 // so we increase '_buffer' by the amount that 'buffer' moved, rather than 95 // assign 'buffer' to '_buffer'. 96 _buffer = _buffer.Slice(bufferStart.Length - buffer.Length); 97 Value = value; 98 Count = count; 99 } 100 101 public readonly bool HasError() 102 { 103 // Check if we have reached the end of the buffer. 104 // 105 // Variable 'count' stores the number of bits in the 'value' buffer, minus 106 // 8. The top byte is part of the algorithm, and the remainder is buffered 107 // to be shifted into it. So if count == 8, the top 16 bits of 'value' are 108 // occupied, 8 for the algorithm and 8 in the buffer. 109 // 110 // When reading a byte from the user's buffer, count is filled with 8 and 111 // one byte is filled into the value buffer. When we reach the end of the 112 // data, count is additionally filled with LotsOfBits. So when 113 // count == LotsOfBits - 1, the user's data has been exhausted. 114 // 115 // 1 if we have tried to decode bits after the end of stream was encountered. 116 // 0 No error. 117 return Count > BdValueSize && Count < LotsOfBits; 118 } 119 120 public int Read(int prob) 121 { 122 uint bit = 0; 123 ulong value; 124 ulong bigsplit; 125 int count; 126 uint range; 127 uint split = (Range * (uint)prob + (256 - (uint)prob)) >> 8; 128 129 if (Count < 0) 130 { 131 Fill(); 132 } 133 134 value = Value; 135 count = Count; 136 137 bigsplit = (ulong)split << (BdValueSize - 8); 138 139 range = split; 140 141 if (value >= bigsplit) 142 { 143 range = Range - split; 144 value -= bigsplit; 145 bit = 1; 146 } 147 148 { 149 int shift = _norm[range]; 150 range <<= shift; 151 value <<= shift; 152 count -= shift; 153 } 154 Value = value; 155 Count = count; 156 Range = range; 157 158 return (int)bit; 159 } 160 161 public int ReadBit() 162 { 163 return Read(128); // vpx_prob_half 164 } 165 166 public int ReadLiteral(int bits) 167 { 168 int literal = 0, bit; 169 170 for (bit = bits - 1; bit >= 0; bit--) 171 { 172 literal |= ReadBit() << bit; 173 } 174 175 return literal; 176 } 177 178 public int ReadTree(ReadOnlySpan<sbyte> tree, ReadOnlySpan<byte> probs) 179 { 180 sbyte i = 0; 181 182 while ((i = tree[i + Read(probs[i >> 1])]) > 0) 183 { 184 } 185 186 return -i; 187 } 188 189 public int ReadBool(int prob, ref ulong value, ref int count, ref uint range) 190 { 191 uint split = (range * (uint)prob + (256 - (uint)prob)) >> 8; 192 ulong bigsplit = (ulong)split << (BdValueSize - 8); 193 194 if (count < 0) 195 { 196 Value = value; 197 Count = count; 198 Fill(); 199 value = Value; 200 count = Count; 201 } 202 203 if (value >= bigsplit) 204 { 205 range -= split; 206 value -= bigsplit; 207 { 208 int shift = _norm[range]; 209 range <<= shift; 210 value <<= shift; 211 count -= shift; 212 } 213 return 1; 214 } 215 range = split; 216 { 217 int shift = _norm[range]; 218 range <<= shift; 219 value <<= shift; 220 count -= shift; 221 } 222 return 0; 223 } 224 225 public ArrayPtr<byte> FindEnd() 226 { 227 // Find the end of the coded buffer 228 while (Count > 8 && Count < BdValueSize) 229 { 230 Count -= 8; 231 _buffer = _buffer.Slice(-1); 232 } 233 return _buffer; 234 } 235 } 236 }