BitReader.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. namespace System.Net.ApiService.Compression.Brotli {
  2. /// <summary>Bit reading helpers.</summary>
  3. internal sealed class BitReader {
  4. /// <summary>
  5. /// Input byte buffer, consist of a ring-buffer and a "slack" region where bytes from the start of
  6. /// the ring-buffer are copied.
  7. /// </summary>
  8. private const int Capacity = 1024;
  9. private const int Slack = 16;
  10. private const int IntBufferSize = Capacity + Slack;
  11. private const int ByteReadSize = Capacity << 2;
  12. private const int ByteBufferSize = IntBufferSize << 2;
  13. private readonly byte[] byteBuffer = new byte[ByteBufferSize];
  14. private readonly int[] intBuffer = new int[IntBufferSize];
  15. private readonly IntReader intReader = new IntReader();
  16. private System.IO.Stream input;
  17. /// <summary>Input stream is finished.</summary>
  18. private bool endOfStreamReached;
  19. /// <summary>Pre-fetched bits.</summary>
  20. internal long accumulator;
  21. /// <summary>Current bit-reading position in accumulator.</summary>
  22. internal int bitOffset;
  23. /// <summary>Offset of next item in intBuffer.</summary>
  24. private int intOffset;
  25. private int tailBytes = 0;
  26. /* Number of bytes in unfinished "int" item. */
  27. /// <summary>Fills up the input buffer.</summary>
  28. /// <remarks>
  29. /// Fills up the input buffer.
  30. /// <p> No-op if there are at least 36 bytes present after current position.
  31. /// <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
  32. /// buffer.
  33. /// </remarks>
  34. internal static void ReadMoreInput(BitReader br) {
  35. // TODO: Split to check and read; move read outside of decoding loop.
  36. if (br.intOffset <= Capacity - 9) {
  37. return;
  38. }
  39. if (br.endOfStreamReached) {
  40. if (IntAvailable(br) >= -2) {
  41. return;
  42. }
  43. throw new BrotliRuntimeException("No more input");
  44. }
  45. int readOffset = br.intOffset << 2;
  46. int bytesRead = ByteReadSize - readOffset;
  47. System.Array.Copy(br.byteBuffer, readOffset, br.byteBuffer, 0, bytesRead);
  48. br.intOffset = 0;
  49. try {
  50. while (bytesRead < ByteReadSize) {
  51. int len = br.input.Read(br.byteBuffer, bytesRead, ByteReadSize - bytesRead);
  52. // EOF is -1 in Java, but 0 in C#.
  53. if (len <= 0) {
  54. br.endOfStreamReached = true;
  55. br.tailBytes = bytesRead;
  56. bytesRead += 3;
  57. break;
  58. }
  59. bytesRead += len;
  60. }
  61. } catch (System.IO.IOException e) {
  62. throw new BrotliRuntimeException("Failed to read input", e);
  63. }
  64. IntReader.Convert(br.intReader, bytesRead >> 2);
  65. }
  66. internal static void CheckHealth(BitReader br, bool endOfStream) {
  67. if (!br.endOfStreamReached) {
  68. return;
  69. }
  70. int byteOffset = (br.intOffset << 2) + ((br.bitOffset + 7) >> 3) - 8;
  71. if (byteOffset > br.tailBytes) {
  72. throw new BrotliRuntimeException("Read after end");
  73. }
  74. if (endOfStream && (byteOffset != br.tailBytes)) {
  75. throw new BrotliRuntimeException("Unused bytes after end");
  76. }
  77. }
  78. /// <summary>Advances the Read buffer by 5 bytes to make room for reading next 24 bits.</summary>
  79. internal static void FillBitWindow(BitReader br) {
  80. if (br.bitOffset >= 32) {
  81. br.accumulator = ((long)br.intBuffer[br.intOffset++] << 32) | ((long)(((ulong)br.accumulator) >> 32));
  82. br.bitOffset -= 32;
  83. }
  84. }
  85. /// <summary>Reads the specified number of bits from Read Buffer.</summary>
  86. internal static int ReadBits(BitReader br, int n) {
  87. FillBitWindow(br);
  88. int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & ((1 << n) - 1);
  89. br.bitOffset += n;
  90. return val;
  91. }
  92. /// <summary>Initialize bit reader.</summary>
  93. /// <remarks>
  94. /// Initialize bit reader.
  95. /// <p> Initialisation turns bit reader to a ready state. Also a number of bytes is prefetched to
  96. /// accumulator. Because of that this method may block until enough data could be read from input.
  97. /// </remarks>
  98. /// <param name="br">BitReader POJO</param>
  99. /// <param name="input">data source</param>
  100. internal static void Init(BitReader br, System.IO.Stream input) {
  101. if (br.input != null) {
  102. throw new System.InvalidOperationException("Bit reader already has associated input stream");
  103. }
  104. IntReader.Init(br.intReader, br.byteBuffer, br.intBuffer);
  105. br.input = input;
  106. br.accumulator = 0;
  107. br.bitOffset = 64;
  108. br.intOffset = Capacity;
  109. br.endOfStreamReached = false;
  110. Prepare(br);
  111. }
  112. private static void Prepare(BitReader br) {
  113. ReadMoreInput(br);
  114. CheckHealth(br, false);
  115. FillBitWindow(br);
  116. FillBitWindow(br);
  117. }
  118. internal static void Reload(BitReader br) {
  119. if (br.bitOffset == 64) {
  120. Prepare(br);
  121. }
  122. }
  123. /// <exception cref="System.IO.IOException"/>
  124. internal static void Close(BitReader br) {
  125. System.IO.Stream @is = br.input;
  126. br.input = null;
  127. if (@is != null) {
  128. @is.Close();
  129. }
  130. }
  131. internal static void JumpToByteBoundary(BitReader br) {
  132. int padding = (64 - br.bitOffset) & 7;
  133. if (padding != 0) {
  134. int paddingBits = BitReader.ReadBits(br, padding);
  135. if (paddingBits != 0) {
  136. throw new BrotliRuntimeException("Corrupted padding bits");
  137. }
  138. }
  139. }
  140. internal static int IntAvailable(BitReader br) {
  141. int limit = Capacity;
  142. if (br.endOfStreamReached) {
  143. limit = (br.tailBytes + 3) >> 2;
  144. }
  145. return limit - br.intOffset;
  146. }
  147. internal static void CopyBytes(BitReader br, byte[] data, int offset, int length) {
  148. if ((br.bitOffset & 7) != 0) {
  149. throw new BrotliRuntimeException("Unaligned copyBytes");
  150. }
  151. // Drain accumulator.
  152. while ((br.bitOffset != 64) && (length != 0)) {
  153. data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
  154. br.bitOffset += 8;
  155. length--;
  156. }
  157. if (length == 0) {
  158. return;
  159. }
  160. // Get data from shadow buffer with "sizeof(int)" granularity.
  161. int copyInts = System.Math.Min(IntAvailable(br), length >> 2);
  162. if (copyInts > 0) {
  163. int readOffset = br.intOffset << 2;
  164. System.Array.Copy(br.byteBuffer, readOffset, data, offset, copyInts << 2);
  165. offset += copyInts << 2;
  166. length -= copyInts << 2;
  167. br.intOffset += copyInts;
  168. }
  169. if (length == 0) {
  170. return;
  171. }
  172. // Read tail bytes.
  173. if (IntAvailable(br) > 0) {
  174. // length = 1..3
  175. FillBitWindow(br);
  176. while (length != 0) {
  177. data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
  178. br.bitOffset += 8;
  179. length--;
  180. }
  181. CheckHealth(br, false);
  182. return;
  183. }
  184. // Now it is possible to copy bytes directly.
  185. try {
  186. while (length > 0) {
  187. int len = br.input.Read(data, offset, length);
  188. if (len == -1) {
  189. throw new BrotliRuntimeException("Unexpected end of input");
  190. }
  191. offset += len;
  192. length -= len;
  193. }
  194. } catch (System.IO.IOException e) {
  195. throw new BrotliRuntimeException("Failed to read input", e);
  196. }
  197. }
  198. }
  199. }