/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 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}