PageRenderTime 482ms CodeModel.GetById 64ms app.highlight 292ms RepoModel.GetById 71ms app.codeStats 2ms

/Utilities/Compression/Inflaters/Inflater.cs

#
C# | 873 lines | 496 code | 84 blank | 293 comment | 59 complexity | 572f27cac0a398b8f8780cc5d3ce924c 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;
  6using Delta.Utilities.Compression.Checksums;
  7using Delta.Utilities.Compression.Deflaters;
  8using Delta.Utilities.Compression.Streams;
  9
 10namespace Delta.Utilities.Compression.Inflaters
 11{
 12	/// <summary>
 13	/// Inflater is used to decompress data that has been compressed according
 14	/// to the "deflate" standard described in rfc1950.
 15	/// <para />
 16	/// The usage is as following.  First you have to set some input with
 17	/// <code>setInput()</code>, then inflate() it.  If inflate doesn't
 18	/// inflate any bytes there may be three reasons:
 19	/// <ul>
 20	/// <li>needsInput() returns true because the input buffer is empty.
 21	/// You have to provide more input with <code>setInput()</code>.
 22	/// NOTE: needsInput() also returns true when, the stream is finished.
 23	/// </li>
 24	/// <li>needsDictionary() returns true, you have to provide a preset
 25	/// dictionary with <code>setDictionary()</code>.</li>
 26	/// <li>finished() returns true, the inflater has finished.</li>
 27	/// </ul>
 28	/// Once the first output byte is produced, a dictionary will not be
 29	/// needed at a later stage.
 30	/// </summary>
 31	public class Inflater
 32	{
 33		#region Constants
 34		/// <summary>
 35		/// Copy lengths for literal codes 257..285
 36		/// </summary>
 37		private static readonly int[] CopyLengths =
 38			{
 39				3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
 40				35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
 41			};
 42
 43		/// <summary>
 44		/// Extra bits for literal codes 257..285
 45		/// </summary>
 46		private static readonly int[] CopyLengthExtraBits =
 47			{
 48				0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
 49				3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
 50			};
 51
 52		/// <summary>
 53		/// Copy offsets for distance codes 0..29
 54		/// </summary>
 55		private static readonly int[] CopyDistanceOffsets =
 56			{
 57				1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
 58				257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
 59				8193, 12289, 16385, 24577
 60			};
 61
 62		/// <summary>
 63		/// Extra bits for distance codes
 64		/// </summary>
 65		private static readonly int[] CopyDistanceExtraBits =
 66			{
 67				0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
 68				7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
 69				12, 12, 13, 13
 70			};
 71
 72		/// <summary>
 73		/// These are the possible states for an inflater to perform decoding.
 74		/// </summary>
 75		private const int
 76			DecodeHeaderState = 0,
 77			DecodeDictionaryState = 1,
 78			DecodeBlocksState = 2,
 79			DecodeStoredLength1 = 3,
 80			DecodeStoredLength2 = 4,
 81			DecodeStored = 5,
 82			DecodeDynamicHeader = 6,
 83			DecodeHuffmanState = 7,
 84			DecodeHuffmanLengthBits = 8,
 85			DecodeHuffmanDistance = 9,
 86			DecodeHuffmanDistanceBits = 10,
 87			DecodeChecksum = 11,
 88			DecodeFinished = 12;
 89		#endregion
 90
 91		#region Public
 92		/// <summary>
 93		/// Returns true, if the input buffer is empty.
 94		/// You should then call setInput(). 
 95		/// NOTE: This method also returns true when the stream is finished.
 96		/// </summary>
 97		public bool IsNeedingInput
 98		{
 99			get
100			{
101				return input.IsNeedingInput;
102			} // get
103		}
104
105		/// <summary>
106		/// Returns true, if a preset dictionary is needed to inflate the input.
107		/// </summary>
108		public bool IsNeedingDictionary
109		{
110			get
111			{
112				return mode == DecodeDictionaryState && neededBits == 0;
113			} // get
114		}
115
116		/// <summary>
117		/// Returns true, if the inflater has finished.  This means, that no
118		/// input is needed and no output can be produced.
119		/// </summary>
120		public bool IsFinished
121		{
122			get
123			{
124				return mode == DecodeFinished && outputWindow.GetAvailable() == 0;
125			} // get
126		}
127
128		/// <summary>
129		/// Gets the adler checksum.  This is either the checksum of all
130		/// uncompressed bytes returned by inflate(), or if needsDictionary()
131		/// returns true (and thus no output was yet produced) this is the
132		/// adler checksum of the expected dictionary.
133		/// </summary>
134		public int Adler
135		{
136			get
137			{
138				return
139					IsNeedingDictionary
140						? readAdler
141						: (int)adler.Value;
142			} // get
143		}
144
145		/// <summary>
146		/// Gets the total number of output bytes returned by inflate().
147		/// </summary>
148		/// <returns>
149		/// the total number of output bytes.
150		/// </returns>
151		public int TotalOut
152		{
153			get
154			{
155				return totalOut;
156			} // get
157		}
158
159		/// <summary>
160		/// Gets the total number of processed compressed input bytes.
161		/// </summary>
162		/// <returns>
163		/// The total number of bytes of processed input bytes.
164		/// </returns>
165		public int TotalIn
166		{
167			get
168			{
169				return totalIn - RemainingInput;
170			} // get
171		}
172
173		/// <summary>
174		/// Gets the number of unprocessed input bytes.  Useful, if the end of the
175		/// stream is reached and you want to further process the bytes after
176		/// the deflate stream.
177		/// </summary>
178		/// <returns>
179		/// The number of bytes of the input which have not been processed.
180		/// </returns>
181		public int RemainingInput
182		{
183			get
184			{
185				return input.AvailableBytes;
186			} // get
187		}
188		#endregion
189
190		#region Private
191		/// <summary>
192		/// This variable contains the current state.
193		/// </summary>
194		private int mode;
195
196		/// <summary>
197		/// The adler checksum of the dictionary or of the decompressed
198		/// stream, as it is written in the header resp. footer of the
199		/// compressed stream. 
200		/// Only valid if mode is DecodeDictionaryState or DecodeChecksum.
201		/// </summary>
202		private int readAdler;
203
204		/// <summary>
205		/// The number of bits needed to complete the current state.  This
206		/// is valid, if mode is DecodeDictionaryState, DecodeChecksum,
207		/// DecodeHuffmanLengthBits or DecodeHuffmanDistanceBits.
208		/// </summary>
209		private int neededBits;
210
211		private int repLength;
212
213		private int repDist;
214
215		private int uncomprLen;
216
217		/// <summary>
218		/// True, if the last block flag was set in the last block of the
219		/// inflated stream.  This means that the stream ends after the
220		/// current block.
221		/// </summary>
222		private bool isLastBlock;
223
224		/// <summary>
225		/// The total number of inflated bytes.
226		/// </summary>
227		private int totalOut;
228
229		/// <summary>
230		/// The total number of bytes set with setInput().  This is not the
231		/// value returned by the TotalIn property, since this also includes the
232		/// unprocessed input.
233		/// </summary>
234		private int totalIn;
235
236		/// <summary>
237		/// This variable stores the noHeader flag that was given to the
238		/// constructor. True means, that the inflated stream doesn't contain a
239		/// Zlib header or footer.
240		/// </summary>
241		private readonly bool noHeader;
242
243		private readonly StreamManipulator input;
244
245		private readonly OutputWindow outputWindow;
246
247		private InflaterDynHeader dynHeader;
248
249		private InflaterHuffmanTree litlenTree, distTree;
250
251		private readonly Adler32 adler;
252		#endregion
253
254		#region Constructors
255		/// <summary>
256		/// Creates a new inflater or RFC1951 decompressor
257		/// RFC1950/Zlib headers and footers will be expected in the input data
258		/// </summary>
259		public Inflater()
260			: this(false)
261		{
262		}
263
264		// Inflater()
265
266		/// <summary>
267		/// Creates a new inflater.
268		/// </summary>
269		/// <param name="noHeader">
270		/// True if no RFC1950/Zlib header and footer fields are expected in the
271		/// input data. This is used for GZIPed/Zipped input. For compatibility
272		/// with Sun JDK you should provide one byte of input more than needed in
273		/// this case.
274		/// </param>
275		public Inflater(bool noHeader)
276		{
277			this.noHeader = noHeader;
278			adler = new Adler32();
279			input = new StreamManipulator();
280			outputWindow = new OutputWindow();
281			mode = noHeader
282			       	? DecodeBlocksState
283			       	: DecodeHeaderState;
284		}
285
286		// Inflater(noHeader)
287		#endregion
288
289		#region Methods
290		/// <summary>
291		/// Resets the inflater so that a new stream can be decompressed.
292		/// All pending input and output will be discarded.
293		/// </summary>
294		public void Reset()
295		{
296			mode = noHeader
297			       	? DecodeBlocksState
298			       	: DecodeHeaderState;
299			totalIn = totalOut = 0;
300			input.Reset();
301			outputWindow.Reset();
302			dynHeader = null;
303			litlenTree = null;
304			distTree = null;
305			isLastBlock = false;
306			adler.Reset();
307		}
308
309		// Reset()
310
311		/// <summary>
312		/// Decodes a zlib/RFC1950 header.
313		/// </summary>
314		/// <returns>False if more input is needed.</returns>
315		/// <exception cref="CompressionException">The header is invalid.
316		/// </exception>
317		private bool DecodeHeader()
318		{
319			int header = input.PeekBits(16);
320			if (header < 0)
321			{
322				return false;
323			} // if (header)
324			input.DropBits(16);
325
326			// The header is written in "wrong" byte order
327			header = ((header << 8) | (header >> 8)) & 0xffff;
328			if (header % 31 != 0)
329			{
330				throw new CompressionException("Header checksum illegal");
331			} // if (header)
332
333			if ((header & 0x0f00) != (Deflater.Deflated << 8))
334			{
335				throw new CompressionException("Compression Method unknown");
336			} // if (header)
337
338			// Maximum size of the backwards window in bits.
339			// We currently ignore this, but we could use it to make the
340			// inflater window more space efficient. On the other hand the
341			// full window (15 bits) is needed most times, anyway.
342			//int max_wbits = ((header & 0x7000) >> 12) + 8;
343
344			if ((header & 0x0020) == 0)
345			{
346				// Dictionary flag?
347				mode = DecodeBlocksState;
348			} // if (header)
349			else
350			{
351				mode = DecodeDictionaryState;
352				neededBits = 32;
353			} // else
354			return true;
355		}
356
357		// DecodeHeader()
358
359		/// <summary>
360		/// Decodes the dictionary checksum after the deflate header.
361		/// </summary>
362		/// <returns>
363		/// False if more input is needed.
364		/// </returns>
365		private bool DecodeDict()
366		{
367			while (neededBits > 0)
368			{
369				int dictByte = input.PeekBits(8);
370				if (dictByte < 0)
371				{
372					return false;
373				} // if (dictByte)
374				input.DropBits(8);
375				readAdler = (readAdler << 8) | dictByte;
376				neededBits -= 8;
377			} // while (neededBits)
378			return false;
379		}
380
381		// DecodeDict()
382
383		/// <summary>
384		/// Decodes the huffman encoded symbols in the input stream.
385		/// </summary>
386		/// <returns>
387		/// false if more input is needed, true if output window is
388		/// full or the current block ends.
389		/// </returns>
390		/// <exception cref="CompressionException">
391		/// if deflated stream is invalid.
392		/// </exception>
393		private bool DecodeHuffman()
394		{
395			int free = outputWindow.GetFreeSpace();
396			while (free >= 258)
397			{
398				int symbol;
399				switch (mode)
400				{
401					case DecodeHuffmanState:
402						// This is the inner loop so it is optimized a bit
403						while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0)
404						{
405							outputWindow.Write(symbol);
406							if (--free < 258)
407							{
408								return true;
409							} // if (--free)
410						} // while ()
411
412						if (symbol < 257)
413						{
414							if (symbol < 0)
415							{
416								return false;
417							} // if (symbol)
418							else
419							{
420								// symbol == 256: end of block
421								distTree = null;
422								litlenTree = null;
423								mode = DecodeBlocksState;
424								return true;
425							} // else
426						} // if (symbol)
427
428						try
429						{
430							repLength = CopyLengths[symbol - 257];
431							neededBits = CopyLengthExtraBits[symbol - 257];
432						} // try
433						catch (Exception)
434						{
435							throw new CompressionException(
436								"Illegal rep length code");
437						} // catch (Exception)
438						// fall through
439						goto case DecodeHuffmanLengthBits;
440
441					case DecodeHuffmanLengthBits:
442						if (neededBits > 0)
443						{
444							mode = DecodeHuffmanLengthBits;
445							int i = input.PeekBits(neededBits);
446							if (i < 0)
447							{
448								return false;
449							} // if (i)
450							input.DropBits(neededBits);
451							repLength += i;
452						} // if (neededBits)
453						mode = DecodeHuffmanDistance;
454						// fall through
455						goto case DecodeHuffmanDistance;
456
457					case DecodeHuffmanDistance:
458						symbol = distTree.GetSymbol(input);
459						if (symbol < 0)
460						{
461							return false;
462						} // if (symbol)
463
464						try
465						{
466							repDist = CopyDistanceOffsets[symbol];
467							neededBits = CopyDistanceExtraBits[symbol];
468						} // try
469						catch (Exception)
470						{
471							throw new CompressionException("Illegal rep dist code");
472						} // catch (Exception)
473
474						// fall through
475						goto case DecodeHuffmanDistanceBits;
476
477					case DecodeHuffmanDistanceBits:
478						if (neededBits > 0)
479						{
480							mode = DecodeHuffmanDistanceBits;
481							int i = input.PeekBits(neededBits);
482							if (i < 0)
483							{
484								return false;
485							} // if (i)
486							input.DropBits(neededBits);
487							repDist += i;
488						} // if (neededBits)
489
490						outputWindow.Repeat(repLength, repDist);
491						free -= repLength;
492						mode = DecodeHuffmanState;
493						break;
494
495					default:
496						throw new CompressionException("Inflater unknown mode");
497				} // switch
498			} // while (free)
499			return true;
500		}
501
502		// DecodeHuffman()
503
504		/// <summary>
505		/// Decodes the adler checksum after the deflate stream.
506		/// </summary>
507		/// <returns>False if more input is needed.</returns>
508		/// <exception cref="CompressionException">If checksum doesn't match.
509		/// </exception>
510		private bool DecodeChksum()
511		{
512			while (neededBits > 0)
513			{
514				int chkByte = input.PeekBits(8);
515				if (chkByte < 0)
516				{
517					return false;
518				} // if (chkByte)
519				input.DropBits(8);
520				readAdler = (readAdler << 8) | chkByte;
521				neededBits -= 8;
522			} // while (neededBits)
523			if ((int)adler.Value != readAdler)
524			{
525				throw new CompressionException(
526					"Adler chksum doesn't match: " + (int)adler.Value +
527					" vs. " + readAdler);
528			} // if (int)
529			mode = DecodeFinished;
530			return false;
531		}
532
533		// DecodeChksum()
534
535		/// <summary>
536		/// Decodes the deflated stream.
537		/// </summary>
538		/// <returns>False if more input is needed, or if finished.</returns>
539		/// <exception cref="CompressionException">If deflated stream is invalid.
540		/// </exception>
541		private bool Decode()
542		{
543			switch (mode)
544			{
545				case DecodeHeaderState:
546					return DecodeHeader();
547				case DecodeDictionaryState:
548					return DecodeDict();
549				case DecodeChecksum:
550					return DecodeChksum();
551
552				case DecodeBlocksState:
553					if (isLastBlock)
554					{
555						if (noHeader)
556						{
557							mode = DecodeFinished;
558							return false;
559						} // if (noHeader)
560						else
561						{
562							input.SkipToByteBoundary();
563							neededBits = 32;
564							mode = DecodeChecksum;
565							return true;
566						} // else
567					} // if (isLastBlock)
568
569					int type = input.PeekBits(3);
570					if (type < 0)
571					{
572						return false;
573					} // if (type)
574					input.DropBits(3);
575					DecodeLastBlockType(type);
576					return true;
577
578				case DecodeStoredLength1:
579				{
580					if ((uncomprLen = input.PeekBits(16)) < 0)
581					{
582						return false;
583					} // if (uncomprLen)
584					input.DropBits(16);
585					mode = DecodeStoredLength2;
586				} // block
587					// fall through
588					goto case DecodeStoredLength2;
589
590				case DecodeStoredLength2:
591				{
592					int nlen = input.PeekBits(16);
593					if (nlen < 0)
594					{
595						return false;
596					} // if (nlen)
597					input.DropBits(16);
598					if (nlen != (uncomprLen ^ 0xffff))
599					{
600						throw new CompressionException(
601							"broken uncompressed block");
602					} // if (nlen)
603					mode = DecodeStored;
604				} // block
605					// fall through
606					goto case DecodeStored;
607
608				case DecodeStored:
609				{
610					int more = outputWindow.CopyStored(input, uncomprLen);
611					uncomprLen -= more;
612					if (uncomprLen == 0)
613					{
614						mode = DecodeBlocksState;
615						return true;
616					} // if (uncomprLen)
617					return !input.IsNeedingInput;
618				} // block
619
620				case DecodeDynamicHeader:
621					if (!dynHeader.Decode(input))
622					{
623						return false;
624					} // if ()
625
626					litlenTree = dynHeader.BuildLitLenTree();
627					distTree = dynHeader.BuildDistTree();
628					mode = DecodeHuffmanState;
629					// fall through
630					goto case DecodeHuffmanState;
631
632				case DecodeHuffmanState:
633				case DecodeHuffmanLengthBits:
634				case DecodeHuffmanDistance:
635				case DecodeHuffmanDistanceBits:
636					return DecodeHuffman();
637
638				case DecodeFinished:
639					return false;
640
641				default:
642					throw new CompressionException(
643						"Inflater.Decode unknown mode");
644			} // switch
645		}
646
647		// Decode()
648
649		/// <summary>
650		/// Decode last block type
651		/// </summary>
652		/// <param name="type">Type</param>
653		private void DecodeLastBlockType(int type)
654		{
655			if ((type & 1) != 0)
656			{
657				isLastBlock = true;
658			} // if (type)
659			switch (type >> 1)
660			{
661				case DeflaterConstants.StoredBlock:
662					input.SkipToByteBoundary();
663					mode = DecodeStoredLength1;
664					break;
665				case DeflaterConstants.StaticTrees:
666					litlenTree = InflaterHuffmanTree.defLitLenTree;
667					distTree = InflaterHuffmanTree.defDistTree;
668					mode = DecodeHuffmanState;
669					break;
670				case DeflaterConstants.DynamicTrees:
671					dynHeader = new InflaterDynHeader();
672					mode = DecodeDynamicHeader;
673					break;
674				default:
675					throw new CompressionException(
676						"Unknown block type " + type);
677			} // switch
678		}
679
680		// DecodeLastBlockType(type)
681
682		/// <summary>
683		/// Sets the preset dictionary.  This should only be called, if
684		/// needsDictionary() returns true and it should set the same
685		/// dictionary, that was used for deflating.  The getAdler()
686		/// function returns the checksum of the dictionary needed.
687		/// </summary>
688		/// <param name="buffer">
689		/// The dictionary.
690		/// </param>
691		public void SetDictionary(byte[] buffer)
692		{
693			SetDictionary(buffer, 0, buffer.Length);
694		}
695
696		// SetDictionary(buffer)
697
698		/// <summary>
699		/// Sets the preset dictionary.  This should only be called, if
700		/// needsDictionary() returns true and it should set the same
701		/// dictionary, that was used for deflating.  The getAdler()
702		/// function returns the checksum of the dictionary needed.
703		/// </summary>
704		/// <param name="buffer">The dictionary.</param>
705		/// <param name="offset">The offset into buffer where the dictionary
706		/// starts.</param>
707		/// <param name="len">The length of the dictionary.</param>
708		/// <exception cref="System.InvalidOperationException">
709		/// No dictionary is needed.
710		/// </exception>
711		/// <exception cref="CompressionException">
712		/// The adler checksum for the buffer is invalid
713		/// </exception>
714		public void SetDictionary(byte[] buffer, int offset, int len)
715		{
716			if (!IsNeedingDictionary)
717			{
718				throw new InvalidOperationException();
719			} // if ()
720
721			adler.Update(buffer, offset, len);
722			if ((int)adler.Value != readAdler)
723			{
724				throw new CompressionException("Wrong adler checksum");
725			} // if (int)
726			adler.Reset();
727			outputWindow.CopyDict(buffer, offset, len);
728			mode = DecodeBlocksState;
729		}
730
731		// SetDictionary(buffer, offset, len)
732
733		/// <summary>
734		/// Sets the input.  This should only be called, if needsInput()
735		/// returns true.
736		/// </summary>
737		/// <param name="buffer">The input buffer.</param>
738		public void SetInput(byte[] buffer)
739		{
740			SetInput(buffer, 0, buffer.Length);
741		}
742
743		// SetInput(buffer)
744
745		/// <summary>
746		/// Sets the input.  This should only be called, if needsInput()
747		/// returns true.
748		/// </summary>
749		/// <param name="buffer">
750		/// The source of input data
751		/// </param>
752		/// <param name="offset">
753		/// The offset into buffer where the input starts.
754		/// </param>
755		/// <param name="length">
756		/// The number of bytes of input to use.
757		/// </param>
758		/// <exception cref="System.InvalidOperationException">
759		/// No input is needed.
760		/// </exception>
761		/// <exception cref="System.ArgumentOutOfRangeException">
762		/// The off and/or len are wrong.
763		/// </exception>
764		public void SetInput(byte[] buffer, int offset, int length)
765		{
766			input.SetInput(buffer, offset, length);
767			totalIn += length;
768		}
769
770		// SetInput(buffer, offset, length)
771
772		/// <summary>
773		/// Inflates the compressed stream to the output buffer.  If this
774		/// returns 0, you should check, whether needsDictionary(),
775		/// needsInput() or finished() returns true, to determine why no
776		/// further output is produced.
777		/// </summary>
778		/// <param name="buffer">The output buffer.</param>
779		/// <returns>
780		/// the number of bytes written to the buffer, 0 if no further
781		/// output can be produced.
782		/// </returns>
783		/// <exception cref="System.ArgumentOutOfRangeException">
784		/// if buf has length 0.
785		/// </exception>
786		/// <exception cref="System.FormatException">
787		/// if deflated stream is invalid.
788		/// </exception>
789		public int Inflate(byte[] buffer)
790		{
791			return Inflate(buffer, 0, buffer.Length);
792		}
793
794		// Inflate(buffer)
795
796		/// <summary>
797		/// Inflates the compressed stream to the output buffer.  If this
798		/// returns 0, you should check, whether needsDictionary(),
799		/// needsInput() or finished() returns true, to determine why no
800		/// further output is produced.
801		/// </summary>
802		/// <param name="buffer">The output buffer.</param>
803		/// <param name="offset">The offset into buffer where the output should
804		/// start.</param>
805		/// <param name="len">The maximum length of the output.</param>
806		/// <returns>The number of bytes written to the buffer, 0 if no further
807		/// output can be produced.</returns>
808		/// <exception cref="System.ArgumentOutOfRangeException">
809		/// if len is &lt;= 0.
810		/// </exception>
811		/// <exception cref="System.ArgumentOutOfRangeException">
812		/// if the offset and/or len are wrong.
813		/// </exception>
814		/// <exception cref="System.FormatException">
815		/// if deflated stream is invalid.
816		/// </exception>
817		public int Inflate(byte[] buffer, int offset, int len)
818		{
819			if (len < 0)
820			{
821				throw new ArgumentOutOfRangeException("len < 0");
822			} // if (len)
823
824			// Special case: len may be zero
825			if (len == 0)
826			{
827				if (IsFinished == false)
828				{
829					// -jr- 08-Nov-2003 INFLATE_BUG fix..
830					Decode();
831				} // if (IsFinished)
832				return 0;
833			} // if (len)
834			/*
835			// Check for correct buff, off, len triple
836			if (off < 0 || off + len >= buf.Length) {
837				throw new ArgumentException("off/len outside buf bounds");
838			}
839			*/
840			int count = 0;
841			int more;
842			do
843			{
844				if (mode != DecodeChecksum)
845				{
846					// Don't give away any output, if we are waiting for the
847					// checksum in the input stream.
848					//
849					// With this trick we have always:
850					//   needsInput() and not finished()
851					//   implies more output can be produced.
852					more = outputWindow.CopyOutput(buffer, offset, len);
853					adler.Update(buffer, offset, more);
854					offset += more;
855					count += more;
856					totalOut += more;
857					len -= more;
858					if (len == 0)
859					{
860						return count;
861					} // if (len)
862				} // if (mode)
863			} while (Decode() ||
864			         (outputWindow.GetAvailable() > 0 &&
865			          mode != DecodeChecksum));
866
867			return count;
868		}
869
870		// Inflate(buf, offset, len)
871		#endregion
872	}
873}