StreamManipulator.cs 6.7 KB

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