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  }