StreamManipulator.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. using System;
  2. namespace System.Net.ApiService.Compression.Zip.Compression.Streams {
  3. /// <summary>
  4. /// This class allows us to retrieve a specified number of bits from
  5. /// the input buffer, as well as copy big byte blocks.
  6. ///
  7. /// It uses an int buffer to store up to 31 bits for direct
  8. /// manipulation. This guarantees that we can get at least 16 bits,
  9. /// but we only need at most 15, so this is all safe.
  10. ///
  11. /// There are some optimizations in this class, for example, you must
  12. /// never peek more than 8 bits more than needed, and you must first
  13. /// peek bits before you may drop them. This is not a general purpose
  14. /// class but optimized for the behaviour of the Inflater.
  15. ///
  16. /// authors of the original java version : John Leuner, Jochen Hoenicke
  17. /// </summary>
  18. public class StreamManipulator
  19. {
  20. /// <summary>
  21. /// Get the next sequence of bits but don't increase input pointer. bitCount must be
  22. /// less or equal 16 and if this call succeeds, you must drop
  23. /// at least n - 8 bits in the next call.
  24. /// </summary>
  25. /// <param name="bitCount">The number of bits to peek.</param>
  26. /// <returns>
  27. /// the value of the bits, or -1 if not enough bits available. */
  28. /// </returns>
  29. public int PeekBits(int bitCount)
  30. {
  31. if (bitsInBuffer_ < bitCount)
  32. {
  33. if (windowStart_ == windowEnd_)
  34. {
  35. return -1; // ok
  36. }
  37. buffer_ |= (uint)((window_[windowStart_++] & 0xff |
  38. (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_);
  39. bitsInBuffer_ += 16;
  40. }
  41. return (int)(buffer_ & ((1 << bitCount) - 1));
  42. }
  43. /// <summary>
  44. /// Tries to grab the next <paramref name="bitCount"/> bits from the input and
  45. /// sets <paramref name="output"/> to the value, adding <paramref name="outputOffset"/>.
  46. /// </summary>
  47. /// <returns>true if enough bits could be read, otherwise false</returns>
  48. public bool TryGetBits(int bitCount, ref int output, int outputOffset = 0)
  49. {
  50. var bits = PeekBits(bitCount);
  51. if (bits < 0)
  52. {
  53. return false;
  54. }
  55. output = bits + outputOffset;
  56. DropBits(bitCount);
  57. return true;
  58. }
  59. /// <summary>
  60. /// Tries to grab the next <paramref name="bitCount"/> bits from the input and
  61. /// sets <paramref name="index"/> of <paramref name="array"/> to the value.
  62. /// </summary>
  63. /// <returns>true if enough bits could be read, otherwise false</returns>
  64. public bool TryGetBits(int bitCount, ref byte[] array, int index)
  65. {
  66. var bits = PeekBits(bitCount);
  67. if (bits < 0)
  68. {
  69. return false;
  70. }
  71. array[index] = (byte)bits;
  72. DropBits(bitCount);
  73. return true;
  74. }
  75. /// <summary>
  76. /// Drops the next n bits from the input. You should have called PeekBits
  77. /// with a bigger or equal n before, to make sure that enough bits are in
  78. /// the bit buffer.
  79. /// </summary>
  80. /// <param name="bitCount">The number of bits to drop.</param>
  81. public void DropBits(int bitCount)
  82. {
  83. buffer_ >>= bitCount;
  84. bitsInBuffer_ -= bitCount;
  85. }
  86. /// <summary>
  87. /// Gets the next n bits and increases input pointer. This is equivalent
  88. /// to <see cref="PeekBits"/> followed by <see cref="DropBits"/>, except for correct error handling.
  89. /// </summary>
  90. /// <param name="bitCount">The number of bits to retrieve.</param>
  91. /// <returns>
  92. /// the value of the bits, or -1 if not enough bits available.
  93. /// </returns>
  94. public int GetBits(int bitCount)
  95. {
  96. int bits = PeekBits(bitCount);
  97. if (bits >= 0)
  98. {
  99. DropBits(bitCount);
  100. }
  101. return bits;
  102. }
  103. /// <summary>
  104. /// Gets the number of bits available in the bit buffer. This must be
  105. /// only called when a previous PeekBits() returned -1.
  106. /// </summary>
  107. /// <returns>
  108. /// the number of bits available.
  109. /// </returns>
  110. public int AvailableBits
  111. {
  112. get
  113. {
  114. return bitsInBuffer_;
  115. }
  116. }
  117. /// <summary>
  118. /// Gets the number of bytes available.
  119. /// </summary>
  120. /// <returns>
  121. /// The number of bytes available.
  122. /// </returns>
  123. public int AvailableBytes
  124. {
  125. get
  126. {
  127. return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3);
  128. }
  129. }
  130. /// <summary>
  131. /// Skips to the next byte boundary.
  132. /// </summary>
  133. public void SkipToByteBoundary()
  134. {
  135. buffer_ >>= (bitsInBuffer_ & 7);
  136. bitsInBuffer_ &= ~7;
  137. }
  138. /// <summary>
  139. /// Returns true when SetInput can be called
  140. /// </summary>
  141. public bool IsNeedingInput
  142. {
  143. get
  144. {
  145. return windowStart_ == windowEnd_;
  146. }
  147. }
  148. /// <summary>
  149. /// Copies bytes from input buffer to output buffer starting
  150. /// at output[offset]. You have to make sure, that the buffer is
  151. /// byte aligned. If not enough bytes are available, copies fewer
  152. /// bytes.
  153. /// </summary>
  154. /// <param name="output">
  155. /// The buffer to copy bytes to.
  156. /// </param>
  157. /// <param name="offset">
  158. /// The offset in the buffer at which copying starts
  159. /// </param>
  160. /// <param name="length">
  161. /// The length to copy, 0 is allowed.
  162. /// </param>
  163. /// <returns>
  164. /// The number of bytes copied, 0 if no bytes were available.
  165. /// </returns>
  166. /// <exception cref="ArgumentOutOfRangeException">
  167. /// Length is less than zero
  168. /// </exception>
  169. /// <exception cref="InvalidOperationException">
  170. /// Bit buffer isnt byte aligned
  171. /// </exception>
  172. public int CopyBytes(byte[] output, int offset, int length)
  173. {
  174. if (length < 0)
  175. {
  176. throw new ArgumentOutOfRangeException(nameof(length));
  177. }
  178. if ((bitsInBuffer_ & 7) != 0)
  179. {
  180. // bits_in_buffer may only be 0 or a multiple of 8
  181. throw new InvalidOperationException("Bit buffer is not byte aligned!");
  182. }
  183. int count = 0;
  184. while ((bitsInBuffer_ > 0) && (length > 0))
  185. {
  186. output[offset++] = (byte)buffer_;
  187. buffer_ >>= 8;
  188. bitsInBuffer_ -= 8;
  189. length--;
  190. count++;
  191. }
  192. if (length == 0)
  193. {
  194. return count;
  195. }
  196. int avail = windowEnd_ - windowStart_;
  197. if (length > avail)
  198. {
  199. length = avail;
  200. }
  201. System.Array.Copy(window_, windowStart_, output, offset, length);
  202. windowStart_ += length;
  203. if (((windowStart_ - windowEnd_) & 1) != 0)
  204. {
  205. // We always want an even number of bytes in input, see peekBits
  206. buffer_ = (uint)(window_[windowStart_++] & 0xff);
  207. bitsInBuffer_ = 8;
  208. }
  209. return count + length;
  210. }
  211. /// <summary>
  212. /// Resets state and empties internal buffers
  213. /// </summary>
  214. public void Reset()
  215. {
  216. buffer_ = 0;
  217. windowStart_ = windowEnd_ = bitsInBuffer_ = 0;
  218. }
  219. /// <summary>
  220. /// Add more input for consumption.
  221. /// Only call when IsNeedingInput returns true
  222. /// </summary>
  223. /// <param name="buffer">data to be input</param>
  224. /// <param name="offset">offset of first byte of input</param>
  225. /// <param name="count">number of bytes of input to add.</param>
  226. public void SetInput(byte[] buffer, int offset, int count)
  227. {
  228. if (buffer == null)
  229. {
  230. throw new ArgumentNullException(nameof(buffer));
  231. }
  232. if (offset < 0)
  233. {
  234. throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative");
  235. }
  236. if (count < 0)
  237. {
  238. throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative");
  239. }
  240. if (windowStart_ < windowEnd_)
  241. {
  242. throw new InvalidOperationException("Old input was not completely processed");
  243. }
  244. int end = offset + count;
  245. // We want to throw an ArrayIndexOutOfBoundsException early.
  246. // Note the check also handles integer wrap around.
  247. if ((offset > end) || (end > buffer.Length))
  248. {
  249. throw new ArgumentOutOfRangeException(nameof(count));
  250. }
  251. if ((count & 1) != 0)
  252. {
  253. // We always want an even number of bytes in input, see PeekBits
  254. buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_);
  255. bitsInBuffer_ += 8;
  256. }
  257. window_ = buffer;
  258. windowStart_ = offset;
  259. windowEnd_ = end;
  260. }
  261. #region Instance Fields
  262. private byte[] window_;
  263. private int windowStart_;
  264. private int windowEnd_;
  265. private uint buffer_;
  266. private int bitsInBuffer_;
  267. #endregion Instance Fields
  268. }
  269. }