PageRenderTime 264ms CodeModel.GetById 141ms app.highlight 8ms RepoModel.GetById 112ms app.codeStats 0ms

/Utilities/Compression/Streams/StreamManipulator.cs

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