| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 | using System;namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams{	/// <summary>	/// This class allows us to retrieve a specified number 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 than 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 behaviour of the Inflater.	///	/// authors of the original java version : John Leuner, Jochen Hoenicke	/// </summary>	public class StreamManipulator	{		/// <summary>		/// Get the next sequence of bits but don't increase input pointer.  bitCount must be		/// less or equal 16 and if this call succeeds, you must drop		/// at least n - 8 bits in the next call.		/// </summary>		/// <param name="bitCount">The number of bits to peek.</param>		/// <returns>		/// the value of the bits, or -1 if not enough bits available.  */		/// </returns>		public int PeekBits(int bitCount)		{			if (bitsInBuffer_ < bitCount) {				if (windowStart_ == windowEnd_) {					return -1; // ok				}				buffer_ |= (uint)((window_[windowStart_++] & 0xff |								 (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_);				bitsInBuffer_ += 16;			}			return (int)(buffer_ & ((1 << bitCount) - 1));		}		/// <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>		/// <param name="bitCount">The number of bits to drop.</param>		public void DropBits(int bitCount)		{			buffer_ >>= bitCount;			bitsInBuffer_ -= bitCount;		}		/// <summary>		/// Gets the next n bits and increases input pointer.  This is equivalent		/// to <see cref="PeekBits"/> followed by <see cref="DropBits"/>, except for correct error handling.		/// </summary>		/// <param name="bitCount">The number of bits to retrieve.</param>		/// <returns>		/// the value of the bits, or -1 if not enough bits available.		/// </returns>		public int GetBits(int bitCount)		{			int bits = PeekBits(bitCount);			if (bits >= 0) {				DropBits(bitCount);			}			return bits;		}		/// <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_;			}		}		/// <summary>		/// Gets the number of bytes available.		/// </summary>		/// <returns>		/// The number of bytes available.		/// </returns>		public int AvailableBytes {			get {				return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3);			}		}		/// <summary>		/// Skips to the next byte boundary.		/// </summary>		public void SkipToByteBoundary()		{			buffer_ >>= (bitsInBuffer_ & 7);			bitsInBuffer_ &= ~7;		}		/// <summary>		/// Returns true when SetInput can be called		/// </summary>		public bool IsNeedingInput {			get {				return windowStart_ == windowEnd_;			}		}		/// <summary>		/// Copies 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 to copy bytes to.		/// </param>		/// <param name="offset">		/// The offset in the buffer at which copying starts		/// </param>		/// <param name="length">		/// The length to copy, 0 is allowed.		/// </param>		/// <returns>		/// The number of bytes copied, 0 if no bytes were available.		/// </returns>		/// <exception cref="ArgumentOutOfRangeException">		/// Length is less than zero		/// </exception>		/// <exception cref="InvalidOperationException">		/// Bit buffer isnt byte aligned		/// </exception>		public int CopyBytes(byte[] output, int offset, int length)		{			if (length < 0) {				throw new ArgumentOutOfRangeException("nameof(length)");			}			if ((bitsInBuffer_ & 7) != 0) {				// bits_in_buffer may only be 0 or a multiple of 8				throw new InvalidOperationException("Bit buffer is not byte aligned!");			}			int count = 0;			while ((bitsInBuffer_ > 0) && (length > 0)) {				output[offset++] = (byte)buffer_;				buffer_ >>= 8;				bitsInBuffer_ -= 8;				length--;				count++;			}			if (length == 0) {				return count;			}			int avail = windowEnd_ - windowStart_;			if (length > avail) {				length = avail;			}			System.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;			}			return count + length;		}		/// <summary>		/// Resets state and empties internal buffers		/// </summary>		public void Reset()		{			buffer_ = 0;			windowStart_ = windowEnd_ = bitsInBuffer_ = 0;		}		/// <summary>		/// Add more input for consumption.		/// Only call when IsNeedingInput returns true		/// </summary>		/// <param name="buffer">data to be input</param>		/// <param name="offset">offset of first byte of input</param>		/// <param name="count">number of bytes of input to add.</param>		public void SetInput(byte[] buffer, int offset, int count)		{			if (buffer == null) {				throw new ArgumentNullException("nameof(buffer)");			}			if (offset < 0) {				throw new ArgumentOutOfRangeException("nameof(offset)", "Cannot be negative");			}			if (count < 0) {				throw new ArgumentOutOfRangeException("nameof(count)", "Cannot be negative");			}			if (windowStart_ < windowEnd_) {				throw new InvalidOperationException("Old input was not completely processed");			}			int end = offset + count;			// We want to throw an ArrayIndexOutOfBoundsException early.			// Note the check also handles integer wrap around.			if ((offset > end) || (end > buffer.Length)) {				throw new ArgumentOutOfRangeException("nameof(count)");			}			if ((count & 1) != 0) {				// We always want an even number of bytes in input, see PeekBits				buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_);				bitsInBuffer_ += 8;			}			window_ = buffer;			windowStart_ = offset;			windowEnd_ = end;		}		#region Instance Fields		private byte[] window_;		private int windowStart_;		private int windowEnd_;		private uint buffer_;		private int bitsInBuffer_;		#endregion	}}
 |