PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Compression/Streams/StreamManipulator.cs

#
C# | 280 lines | 161 code | 25 blank | 94 comment | 20 complexity | a9f0f81e5b3010bedc6f74fb99f500fa MD5 | raw file
Possible License(s): Apache-2.0
  1. // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
  2. // Authors of the original java version: Jochen Hoenicke, John Leuner
  3. // See http://www.ISeeSharpCode.com for more information.
  4. using System;
  5. namespace Delta.Utilities.Compression.Streams
  6. {
  7. /// <summary>
  8. /// This class allows us to retrieve a specified amount of bits from
  9. /// the input buffer, as well as copy big byte blocks.
  10. ///
  11. /// It uses an int buffer to store up to 31 bits for direct
  12. /// manipulation. This guarantees that we can get at least 16 bits,
  13. /// but we only need at most 15, so this is all safe.
  14. ///
  15. /// There are some optimizations in this class, for example, you must
  16. /// never peek more then 8 bits more than needed, and you must first
  17. /// peek bits before you may drop them. This is not a general purpose
  18. /// class but optimized for the behavior of the Inflater.
  19. /// </summary>
  20. public class StreamManipulator
  21. {
  22. #region AvailableBits (Public)
  23. /// <summary>
  24. /// Gets the number of bits available in the bit buffer. This must be
  25. /// only called when a previous peekBits() returned -1.
  26. /// </summary>
  27. /// <returns>The number of bits available.</returns>
  28. public int AvailableBits
  29. {
  30. get
  31. {
  32. return bitsInBuffer;
  33. } // get
  34. }
  35. #endregion
  36. #region AvailableBytes (Public)
  37. /// <summary>
  38. /// Gets the number of bytes available.
  39. /// </summary>
  40. /// <returns>The number of bytes available.</returns>
  41. public int AvailableBytes
  42. {
  43. get
  44. {
  45. return windowEnd - windowStart + (bitsInBuffer >> 3);
  46. } // get
  47. }
  48. #endregion
  49. #region IsNeedingInput (Public)
  50. /// <summary>
  51. /// Is needing input
  52. /// </summary>
  53. /// <returns>
  54. /// True if we need input, false if the start window is already at the end.
  55. /// </returns>
  56. public bool IsNeedingInput
  57. {
  58. get
  59. {
  60. return windowStart == windowEnd;
  61. } // get
  62. }
  63. #endregion
  64. #region Private
  65. #region window (Private)
  66. /// <summary>
  67. /// Window
  68. /// </summary>
  69. private byte[] window;
  70. #endregion
  71. #region windowStart (Private)
  72. /// <summary>
  73. /// Window start
  74. /// </summary>
  75. private int windowStart;
  76. #endregion
  77. #region windowEnd (Private)
  78. /// <summary>
  79. /// Window end
  80. /// </summary>
  81. private int windowEnd;
  82. #endregion
  83. #region buffer (Private)
  84. /// <summary>
  85. /// Buffer
  86. /// </summary>
  87. private uint buffer;
  88. #endregion
  89. #region bitsInBuffer (Private)
  90. /// <summary>
  91. /// Bits in buffer
  92. /// </summary>
  93. private int bitsInBuffer;
  94. #endregion
  95. #endregion
  96. #region PeekBits (Public)
  97. /// <summary>
  98. /// Get the next n bits but don't increase input pointer. n must be
  99. /// less or equal 16 and if you if this call succeeds, you must drop
  100. /// at least n-8 bits in the next call.
  101. /// </summary>
  102. /// <returns>
  103. /// the value of the bits, or -1 if not enough bits available. */
  104. /// </returns>
  105. public int PeekBits(int n)
  106. {
  107. if (bitsInBuffer < n)
  108. {
  109. if (windowStart == windowEnd)
  110. {
  111. return -1; // ok
  112. } // if (windowStart)
  113. buffer |= (uint)((window[windowStart++] & 0xff |
  114. (window[windowStart++] & 0xff) << 8) << bitsInBuffer);
  115. bitsInBuffer += 16;
  116. } // if (bitsInBuffer)
  117. return (int)(buffer & ((1 << n) - 1));
  118. }
  119. #endregion
  120. #region DropBits (Public)
  121. /// <summary>
  122. /// Drops the next n bits from the input. You should have called peekBits
  123. /// with a bigger or equal n before, to make sure that enough bits are in
  124. /// the bit buffer.
  125. /// </summary>
  126. public void DropBits(int n)
  127. {
  128. buffer >>= n;
  129. bitsInBuffer -= n;
  130. }
  131. #endregion
  132. #region GetBits (Public)
  133. /// <summary>
  134. /// Gets the next n bits and increases input pointer. This is equivalent
  135. /// to peekBits followed by dropBits, except for correct error handling.
  136. /// </summary>
  137. /// <returns>
  138. /// the value of the bits, or -1 if not enough bits available.
  139. /// </returns>
  140. public int GetBits(int n)
  141. {
  142. int bits = PeekBits(n);
  143. if (bits >= 0)
  144. {
  145. DropBits(n);
  146. } // if (bits)
  147. return bits;
  148. }
  149. #endregion
  150. #region SkipToByteBoundary (Public)
  151. /// <summary>
  152. /// Skips to the next byte boundary.
  153. /// </summary>
  154. public void SkipToByteBoundary()
  155. {
  156. buffer >>= (bitsInBuffer & 7);
  157. bitsInBuffer &= ~7;
  158. }
  159. #endregion
  160. #region CopyBytes (Public)
  161. /// <summary>
  162. /// Copies length bytes from input buffer to output buffer starting
  163. /// at output[offset]. You have to make sure, that the buffer is
  164. /// byte aligned. If not enough bytes are available, copies fewer
  165. /// bytes.
  166. /// </summary>
  167. /// <param name="output">The buffer.</param>
  168. /// <param name="offset">The offset in the buffer.</param>
  169. /// <param name="length">the length to copy, 0 is allowed.</param>
  170. /// <returns>The number of bytes copied, 0 if no byte is available.
  171. /// </returns>
  172. public int CopyBytes(byte[] output, int offset, int length)
  173. {
  174. if (length < 0)
  175. {
  176. throw new ArgumentOutOfRangeException("length negative");
  177. } // if (length)
  178. if ((bitsInBuffer & 7) != 0)
  179. {
  180. // bitsInBuffer may only be 0 or 8
  181. throw new InvalidOperationException("Bit buffer is not aligned!");
  182. } // if (bitsInBuffer)
  183. int count = 0;
  184. while (bitsInBuffer > 0 &&
  185. length > 0)
  186. {
  187. output[offset++] = (byte)buffer;
  188. buffer >>= 8;
  189. bitsInBuffer -= 8;
  190. length--;
  191. count++;
  192. } // while (bitsInBuffer)
  193. if (length == 0)
  194. {
  195. return count;
  196. } // if (length)
  197. int avail = windowEnd - windowStart;
  198. if (length > avail)
  199. {
  200. length = avail;
  201. } // if (length)
  202. Array.Copy(window, windowStart, output, offset, length);
  203. windowStart += length;
  204. if (((windowStart - windowEnd) & 1) != 0)
  205. {
  206. // We always want an even number of bytes in input, see peekBits
  207. buffer = (uint)(window[windowStart++] & 0xff);
  208. bitsInBuffer = 8;
  209. } // if (windowStart)
  210. return count + length;
  211. }
  212. #endregion
  213. #region Reset (Public)
  214. /// <summary>
  215. /// Reset
  216. /// </summary>
  217. public void Reset()
  218. {
  219. buffer = (uint)(windowStart = windowEnd = bitsInBuffer = 0);
  220. }
  221. #endregion
  222. #region SetInput (Public)
  223. /// <summary>
  224. /// Set input
  225. /// </summary>
  226. /// <param name="buf">Buf</param>
  227. /// <param name="off">Off</param>
  228. /// <param name="len">Len</param>
  229. public void SetInput(byte[] buf, int off, int len)
  230. {
  231. if (windowStart < windowEnd)
  232. {
  233. throw new InvalidOperationException(
  234. "Old input was not completely processed");
  235. } // if (windowStart)
  236. int end = off + len;
  237. // We want to throw an ArrayIndexOutOfBoundsException early.
  238. // The check is very tricky: it also handles integer wrap around.
  239. if (0 > off || off > end || end > buf.Length)
  240. {
  241. throw new ArgumentOutOfRangeException();
  242. } // if (0)
  243. if ((len & 1) != 0)
  244. {
  245. // We always want an even number of bytes in input, see peekBits
  246. buffer |= (uint)((buf[off++] & 0xff) << bitsInBuffer);
  247. bitsInBuffer += 8;
  248. } // if (len)
  249. window = buf;
  250. windowStart = off;
  251. windowEnd = end;
  252. }
  253. #endregion
  254. }
  255. }