Reader.cs 7.5 KB

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