/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
- // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
- // Authors of the original java version: Jochen Hoenicke, John Leuner
- // See http://www.ISeeSharpCode.com for more information.
-
- using System;
-
- namespace Delta.Utilities.Compression.Streams
- {
- /// <summary>
- /// This class allows us to retrieve a specified amount of bits from
- /// the input buffer, as well as copy big byte blocks.
- ///
- /// It uses an int buffer to store up to 31 bits for direct
- /// manipulation. This guarantees that we can get at least 16 bits,
- /// but we only need at most 15, so this is all safe.
- ///
- /// There are some optimizations in this class, for example, you must
- /// never peek more then 8 bits more than needed, and you must first
- /// peek bits before you may drop them. This is not a general purpose
- /// class but optimized for the behavior of the Inflater.
- /// </summary>
- public class StreamManipulator
- {
- #region AvailableBits (Public)
- /// <summary>
- /// Gets the number of bits available in the bit buffer. This must be
- /// only called when a previous peekBits() returned -1.
- /// </summary>
- /// <returns>The number of bits available.</returns>
- public int AvailableBits
- {
- get
- {
- return bitsInBuffer;
- } // get
- }
- #endregion
-
- #region AvailableBytes (Public)
- /// <summary>
- /// Gets the number of bytes available.
- /// </summary>
- /// <returns>The number of bytes available.</returns>
- public int AvailableBytes
- {
- get
- {
- return windowEnd - windowStart + (bitsInBuffer >> 3);
- } // get
- }
- #endregion
-
- #region IsNeedingInput (Public)
- /// <summary>
- /// Is needing input
- /// </summary>
- /// <returns>
- /// True if we need input, false if the start window is already at the end.
- /// </returns>
- public bool IsNeedingInput
- {
- get
- {
- return windowStart == windowEnd;
- } // get
- }
- #endregion
-
- #region Private
-
- #region window (Private)
- /// <summary>
- /// Window
- /// </summary>
- private byte[] window;
- #endregion
-
- #region windowStart (Private)
- /// <summary>
- /// Window start
- /// </summary>
- private int windowStart;
- #endregion
-
- #region windowEnd (Private)
- /// <summary>
- /// Window end
- /// </summary>
- private int windowEnd;
- #endregion
-
- #region buffer (Private)
- /// <summary>
- /// Buffer
- /// </summary>
- private uint buffer;
- #endregion
-
- #region bitsInBuffer (Private)
- /// <summary>
- /// Bits in buffer
- /// </summary>
- private int bitsInBuffer;
- #endregion
-
- #endregion
-
- #region PeekBits (Public)
- /// <summary>
- /// Get the next n bits but don't increase input pointer. n must be
- /// less or equal 16 and if you if this call succeeds, you must drop
- /// at least n-8 bits in the next call.
- /// </summary>
- /// <returns>
- /// the value of the bits, or -1 if not enough bits available. */
- /// </returns>
- public int PeekBits(int n)
- {
- if (bitsInBuffer < n)
- {
- if (windowStart == windowEnd)
- {
- return -1; // ok
- } // if (windowStart)
- buffer |= (uint)((window[windowStart++] & 0xff |
- (window[windowStart++] & 0xff) << 8) << bitsInBuffer);
- bitsInBuffer += 16;
- } // if (bitsInBuffer)
- return (int)(buffer & ((1 << n) - 1));
- }
- #endregion
-
- #region DropBits (Public)
- /// <summary>
- /// Drops the next n bits from the input. You should have called peekBits
- /// with a bigger or equal n before, to make sure that enough bits are in
- /// the bit buffer.
- /// </summary>
- public void DropBits(int n)
- {
- buffer >>= n;
- bitsInBuffer -= n;
- }
- #endregion
-
- #region GetBits (Public)
- /// <summary>
- /// Gets the next n bits and increases input pointer. This is equivalent
- /// to peekBits followed by dropBits, except for correct error handling.
- /// </summary>
- /// <returns>
- /// the value of the bits, or -1 if not enough bits available.
- /// </returns>
- public int GetBits(int n)
- {
- int bits = PeekBits(n);
- if (bits >= 0)
- {
- DropBits(n);
- } // if (bits)
- return bits;
- }
- #endregion
-
- #region SkipToByteBoundary (Public)
- /// <summary>
- /// Skips to the next byte boundary.
- /// </summary>
- public void SkipToByteBoundary()
- {
- buffer >>= (bitsInBuffer & 7);
- bitsInBuffer &= ~7;
- }
- #endregion
-
- #region CopyBytes (Public)
- /// <summary>
- /// Copies length bytes from input buffer to output buffer starting
- /// at output[offset]. You have to make sure, that the buffer is
- /// byte aligned. If not enough bytes are available, copies fewer
- /// bytes.
- /// </summary>
- /// <param name="output">The buffer.</param>
- /// <param name="offset">The offset in the buffer.</param>
- /// <param name="length">the length to copy, 0 is allowed.</param>
- /// <returns>The number of bytes copied, 0 if no byte is available.
- /// </returns>
- public int CopyBytes(byte[] output, int offset, int length)
- {
- if (length < 0)
- {
- throw new ArgumentOutOfRangeException("length negative");
- } // if (length)
- if ((bitsInBuffer & 7) != 0)
- {
- // bitsInBuffer may only be 0 or 8
- throw new InvalidOperationException("Bit buffer is not aligned!");
- } // if (bitsInBuffer)
-
- int count = 0;
- while (bitsInBuffer > 0 &&
- length > 0)
- {
- output[offset++] = (byte)buffer;
- buffer >>= 8;
- bitsInBuffer -= 8;
- length--;
- count++;
- } // while (bitsInBuffer)
- if (length == 0)
- {
- return count;
- } // if (length)
-
- int avail = windowEnd - windowStart;
- if (length > avail)
- {
- length = avail;
- } // if (length)
- Array.Copy(window, windowStart, output, offset, length);
- windowStart += length;
-
- if (((windowStart - windowEnd) & 1) != 0)
- {
- // We always want an even number of bytes in input, see peekBits
- buffer = (uint)(window[windowStart++] & 0xff);
- bitsInBuffer = 8;
- } // if (windowStart)
- return count + length;
- }
- #endregion
-
- #region Reset (Public)
- /// <summary>
- /// Reset
- /// </summary>
- public void Reset()
- {
- buffer = (uint)(windowStart = windowEnd = bitsInBuffer = 0);
- }
- #endregion
-
- #region SetInput (Public)
- /// <summary>
- /// Set input
- /// </summary>
- /// <param name="buf">Buf</param>
- /// <param name="off">Off</param>
- /// <param name="len">Len</param>
- public void SetInput(byte[] buf, int off, int len)
- {
- if (windowStart < windowEnd)
- {
- throw new InvalidOperationException(
- "Old input was not completely processed");
- } // if (windowStart)
-
- int end = off + len;
-
- // We want to throw an ArrayIndexOutOfBoundsException early.
- // The check is very tricky: it also handles integer wrap around.
- if (0 > off || off > end || end > buf.Length)
- {
- throw new ArgumentOutOfRangeException();
- } // if (0)
-
- if ((len & 1) != 0)
- {
- // We always want an even number of bytes in input, see peekBits
- buffer |= (uint)((buf[off++] & 0xff) << bitsInBuffer);
- bitsInBuffer += 8;
- } // if (len)
-
- window = buf;
- windowStart = off;
- windowEnd = end;
- }
- #endregion
- }
- }