PageRenderTime 142ms CodeModel.GetById 122ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

/Utilities/Compression/Deflaters/DeflaterEngine.cs

#
C# | 937 lines | 606 code | 97 blank | 234 comment | 98 complexity | 2d1e4feae1e961ca51f7cc678255273e 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;
  7
  8namespace Delta.Utilities.Compression.Deflaters
  9{
 10	/// <summary>
 11	/// Low level compression engine for deflate algorithm which uses a
 12	/// 32K sliding window with secondary compression from
 13	/// Huffman/Shannon-Fano codes.
 14	/// </summary>
 15	/// <remarks>
 16	/// DEFLATE ALGORITHM:
 17	///
 18	/// The uncompressed stream is inserted into the window array.  When
 19	/// the window array is full the first half is thrown away and the
 20	/// second half is copied to the beginning.
 21	///
 22	/// The head array is a hash table.  Three characters build a hash value
 23	/// and they the value points to the corresponding index in window of 
 24	/// the last string with this hash.  The prev array implements a
 25	/// linked list of matches with the same hash: prev[index &amp; WMask] points
 26	/// to the previous index with the same hash.
 27	/// </remarks>
 28	public class DeflaterEngine : DeflaterConstants
 29	{
 30		#region Constants
 31		/// <summary>
 32		/// Length too far, reached limit at 4096 bytes.
 33		/// </summary>
 34		private const int LengthTooFar = 4096;
 35		#endregion
 36
 37		#region Adler (Public)
 38		/// <summary>
 39		/// Get current value of Adler checksum
 40		/// </summary>		
 41		public int Adler
 42		{
 43			get
 44			{
 45				return (int)adler.Value;
 46			}
 47		}
 48		#endregion
 49
 50		#region TotalIn (Public)
 51		/// <summary>
 52		/// Total data processed
 53		/// </summary>		
 54		public int TotalIn
 55		{
 56			get
 57			{
 58				return totalIn;
 59			}
 60		}
 61		#endregion
 62
 63		#region Strategy (Public)
 64		/// <summary>
 65		/// Get/set the <see cref="DeflateStrategy">deflate strategy</see>
 66		/// </summary>		
 67		public DeflateStrategy Strategy
 68		{
 69			get
 70			{
 71				return strategy;
 72			}
 73			set
 74			{
 75				strategy = value;
 76			}
 77		}
 78		#endregion
 79
 80		#region Private
 81
 82		#region ins_h (Private)
 83		/// <summary>
 84		/// Ins _h
 85		/// </summary>
 86		private int ins_h;
 87		#endregion
 88
 89		#region head (Private)
 90		/// <summary>
 91		/// Hashtable, hashing three characters to an index for window, so
 92		/// that window[index]..window[index+2] have this hash code.  
 93		/// Note that the array should really be unsigned short, so you need
 94		/// to and the values with 0xffff.
 95		/// </summary>
 96		private readonly short[] head;
 97		#endregion
 98
 99		#region prev (Private)
100		/// <summary>
101		/// <code>prev[index &amp; WMask]</code> points to the previous index that
102		/// has the same hash code as the string starting at index.
103		/// This way entries with the same hash code are in a linked list.
104		/// Note that the array should really be unsigned short, so you need
105		/// to and the values with 0xffff.
106		/// </summary>
107		private readonly short[] prev;
108		#endregion
109
110		#region matchStart (Private)
111		/// <summary>
112		/// Match Start
113		/// </summary>
114		private int matchStart;
115		#endregion
116
117		#region matchLen (Private)
118		/// <summary>
119		/// Match Length
120		/// </summary>
121		private int matchLen;
122		#endregion
123
124		#region prevAvailable (Private)
125		/// <summary>
126		/// Prev Available
127		/// </summary>
128		private bool prevAvailable;
129		#endregion
130
131		#region blockStart (Private)
132		/// <summary>
133		/// Block Start
134		/// </summary>
135		private int blockStart;
136		#endregion
137
138		#region strstart (Private)
139		/// <summary>
140		/// String Start
141		/// </summary>
142		private int strstart;
143		#endregion
144
145		#region lookahead (Private)
146		/// <summary>
147		/// lookahead is the number of characters starting at strstart in
148		/// window that are valid.
149		/// So window[strstart] until window[strstart+lookahead-1] are valid
150		/// characters.
151		/// </summary>
152		private int lookahead;
153		#endregion
154
155		#region window (Private)
156		/// <summary>
157		/// This array contains the part of the uncompressed stream that 
158		/// is of relevance.  The current character is indexed by strstart.
159		/// </summary>
160		private readonly byte[] window;
161		#endregion
162
163		#region strategy (Private)
164		/// <summary>
165		/// Deflate Strategy
166		/// </summary>
167		private DeflateStrategy strategy;
168		#endregion
169
170		#region max_chain (Private)
171		/// <summary>
172		/// Points to the current character in the window.
173		/// </summary>
174		private int max_chain;
175		#endregion
176
177		#region max_lazy (Private)
178		/// <summary>
179		/// Points to the current character in the window.
180		/// </summary>
181		private int max_lazy;
182		#endregion
183
184		#region niceLength (Private)
185		/// <summary>
186		/// Points to the current character in the window.
187		/// </summary>
188		private int niceLength;
189		#endregion
190
191		#region goodLength (Private)
192		/// <summary>
193		/// Points to the current character in the window.
194		/// </summary>
195		private int goodLength;
196		#endregion
197
198		#region comprFunc (Private)
199		/// <summary>
200		/// The current compression function.
201		/// </summary>
202		private int comprFunc;
203		#endregion
204
205		#region inputBuf (Private)
206		/// <summary>
207		/// The input data for compression.
208		/// </summary>
209		private byte[] inputBuf;
210		#endregion
211
212		#region totalIn (Private)
213		/// <summary>
214		/// The total bytes of input read.
215		/// </summary>
216		private int totalIn;
217		#endregion
218
219		#region inputOff (Private)
220		/// <summary>
221		/// The offset into inputBuf, where input data starts.
222		/// </summary>
223		private int inputOff;
224		#endregion
225
226		#region inputEnd (Private)
227		/// <summary>
228		/// The end offset of the input data.
229		/// </summary>
230		private int inputEnd;
231		#endregion
232
233		#region pending (Private)
234		/// <summary>
235		/// Pending
236		/// </summary>
237		private readonly DeflaterPending pending;
238		#endregion
239
240		#region huffman (Private)
241		/// <summary>
242		/// Huffman
243		/// </summary>
244		private readonly DeflaterHuffman huffman;
245		#endregion
246
247		#region adler (Private)
248		/// <summary>
249		/// The adler checksum
250		/// </summary>
251		private readonly Adler32 adler;
252		#endregion
253
254		#endregion
255
256		#region Constructors
257		/// <summary>
258		/// Construct instance with pending buffer
259		/// </summary>
260		/// <param name="setPending">
261		/// Pending buffer to use
262		/// </param>>
263		public DeflaterEngine(DeflaterPending setPending)
264		{
265			pending = setPending;
266			huffman = new DeflaterHuffman(setPending);
267			adler = new Adler32();
268
269			window = new byte[2 * WSize];
270			head = new short[HashSize];
271			prev = new short[WSize];
272
273			// We start at index 1, to avoid an implementation deficiency, that
274			// we cannot build a repeat pattern at index 0.
275			blockStart = strstart = 1;
276		}
277		#endregion
278
279		#region Reset (Public)
280		/// <summary>
281		/// Reset internal state
282		/// </summary>		
283		public void Reset()
284		{
285			huffman.Reset();
286			adler.Reset();
287			blockStart = strstart = 1;
288			lookahead = 0;
289			totalIn = 0;
290			prevAvailable = false;
291			matchLen = MinMatch - 1;
292
293			for (int i = 0; i < HashSize; i++)
294			{
295				head[i] = 0;
296			}
297
298			for (int i = 0; i < WSize; i++)
299			{
300				prev[i] = 0;
301			}
302		}
303		#endregion
304
305		#region ResetAdler (Public)
306		/// <summary>
307		/// Reset Adler checksum
308		/// </summary>		
309		public void ResetAdler()
310		{
311			adler.Reset();
312		}
313		#endregion
314
315		#region SetLevel (Public)
316		/// <summary>
317		/// Set the deflate level (0-9)
318		/// </summary>
319		public void SetLevel(int lvl)
320		{
321			// Good length
322			goodLength = GoodLength[lvl];
323			max_lazy = MaxLazy[lvl];
324			// Nice length
325			niceLength = NiceLength[lvl];
326			max_chain = MaxChain[lvl];
327
328			if (CompressionFunction[lvl] != comprFunc)
329			{
330				switch (comprFunc)
331				{
332					case CompressionDeflateStored:
333						if (strstart > blockStart)
334						{
335							huffman.FlushStoredBlock(window, blockStart,
336								strstart - blockStart, false);
337							blockStart = strstart;
338						}
339						UpdateHash();
340						break;
341					case CompressionDeflateFast:
342						if (strstart > blockStart)
343						{
344							huffman.FlushBlock(window, blockStart, strstart - blockStart,
345								false);
346							blockStart = strstart;
347						}
348						break;
349					case CompressionDeflateSlow:
350						if (prevAvailable)
351						{
352							huffman.TallyLit(window[strstart - 1] & 0xff);
353						}
354						if (strstart > blockStart)
355						{
356							huffman.FlushBlock(window, blockStart, strstart - blockStart, false);
357							blockStart = strstart;
358						}
359						prevAvailable = false;
360						matchLen = MinMatch - 1;
361						break;
362				}
363				comprFunc = CompressionFunction[lvl];
364			}
365		}
366		#endregion
367
368		#region FillWindow (Public)
369		/// <summary>
370		/// Fill the window
371		/// </summary>
372		public void FillWindow()
373		{
374			// If the window is almost full and there is insufficient lookahead,
375			// move the upper half to the lower one to make room in the upper half.
376			if (strstart >= WSize + MaxDistance)
377			{
378				SlideWindow();
379			}
380
381			// If there is not enough lookahead, but still some input left,
382			// read in the input.
383			while (lookahead < MinLookAhead &&
384			       inputOff < inputEnd)
385			{
386				int more = 2 * WSize - lookahead - strstart;
387
388				if (more > inputEnd - inputOff)
389				{
390					more = inputEnd - inputOff;
391				}
392
393				Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more);
394				adler.Update(inputBuf, inputOff, more);
395
396				inputOff += more;
397				totalIn += more;
398				lookahead += more;
399			}
400
401			if (lookahead >= MinMatch)
402			{
403				UpdateHash();
404			}
405		}
406		#endregion
407
408		#region SetDictionary (Public)
409		/// <summary>
410		/// Set compression dictionary
411		/// </summary>
412		public void SetDictionary(byte[] buffer, int offset, int length)
413		{
414			adler.Update(buffer, offset, length);
415			if (length < MinMatch)
416			{
417				return;
418			}
419			if (length > MaxDistance)
420			{
421				offset += length - MaxDistance;
422				length = MaxDistance;
423			}
424
425			Array.Copy(buffer, offset, window, strstart, length);
426
427			UpdateHash();
428			--length;
429			while (--length > 0)
430			{
431				InsertString();
432				strstart++;
433			}
434			strstart += 2;
435			blockStart = strstart;
436		}
437		#endregion
438
439		#region Deflate (Public)
440		/// <summary>
441		/// Deflate drives actual compression of data
442		/// </summary>
443		public bool Deflate(bool flush, bool finish)
444		{
445			bool progress;
446			do
447			{
448				FillWindow();
449				bool canFlush = flush && inputOff == inputEnd;
450				switch (comprFunc)
451				{
452					case CompressionDeflateStored:
453						progress = DeflateStored(canFlush, finish);
454						break;
455					case CompressionDeflateFast:
456						progress = DeflateFast(canFlush, finish);
457						break;
458					case CompressionDeflateSlow:
459						progress = DeflateSlow(canFlush, finish);
460						break;
461					default:
462						throw new InvalidOperationException("unknown comprFunc");
463				}
464				// Repeat while we have no pending output and progress was made
465			} while (pending.IsFlushed && progress);
466			return progress;
467		}
468		#endregion
469
470		#region SetInput (Public)
471		/// <summary>
472		/// Sets input data to be deflated.
473		/// Should only be called when <code>NeedsInput()</code> returns true.
474		/// </summary>
475		/// <param name="buf">The buffer containing input data.</param>
476		/// <param name="off">The index of the first byte of data.</param>
477		/// <param name="len">The number of bytes of data to use as input.</param>
478		public void SetInput(byte[] buf, int off, int len)
479		{
480			if (inputOff < inputEnd)
481			{
482				throw new InvalidOperationException(
483					"Old input was not completely processed");
484			}
485
486			int end = off + len;
487
488			// We want to throw an ArrayIndexOutOfBoundsException early.
489			// The check is very tricky: it also handles integer wrap around.
490			if (0 > off ||
491			    off > end ||
492			    end > buf.Length)
493			{
494				throw new ArgumentOutOfRangeException();
495			}
496
497			inputBuf = buf;
498			inputOff = off;
499			inputEnd = end;
500		}
501		#endregion
502
503		#region NeedsInput (Public)
504		/// <summary>
505		/// Return true if input is needed via <see cref="SetInput">SetInput</see>
506		/// </summary>		
507		public bool NeedsInput()
508		{
509			return inputEnd == inputOff;
510		}
511		#endregion
512
513		#region Methods (Private)
514
515		#region UpdateHash
516		/// <summary>
517		/// Update hash
518		/// </summary>
519		private void UpdateHash()
520		{
521			ins_h = (window[strstart] << HashShift) ^ window[strstart + 1];
522		}
523		#endregion
524
525		#region InsertString
526		/// <summary>
527		/// Inserts the current string in the head hash and returns the previous
528		/// value for this hash.
529		/// </summary>
530		/// <returns>The previous hash value</returns>
531		private int InsertString()
532		{
533			short match;
534			int hash =
535				((ins_h << HashShift) ^ window[strstart + (MinMatch - 1)]) &
536				HashMask;
537
538			prev[strstart & WMask] = match = head[hash];
539			head[hash] = (short)strstart;
540			ins_h = hash;
541			return match & 0xffff;
542		}
543		#endregion
544
545		#region SlideWindow
546		/// <summary>
547		/// Slide window
548		/// </summary>
549		private void SlideWindow()
550		{
551			Array.Copy(window, WSize, window, 0, WSize);
552			matchStart -= WSize;
553			strstart -= WSize;
554			blockStart -= WSize;
555
556			// Slide the hash table (could be avoided with 32 bit values
557			// at the expense of memory usage).
558			for (int i = 0; i < HashSize; ++i)
559			{
560				int m = head[i] & 0xffff;
561				head[i] = (short)(m >= WSize
562				                  	? (m - WSize)
563				                  	: 0);
564			}
565
566			// Slide the prev table.
567			for (int i = 0; i < WSize; i++)
568			{
569				int m = prev[i] & 0xffff;
570				prev[i] = (short)(m >= WSize
571				                  	? (m - WSize)
572				                  	: 0);
573			}
574		}
575		#endregion
576
577		#region FindLongestMatch
578		/// <summary>
579		/// Find the best (longest) string in the window matching the 
580		/// string starting at strstart.
581		///
582		/// Preconditions:
583		/// <code>
584		/// strstart + MaxMatch &lt;= window.length.</code>
585		/// </summary>
586		/// <param name="curMatch"></param>
587		/// <returns>True if a match greater than the minimum length is found</returns>
588		private bool FindLongestMatch(int curMatch)
589		{
590			int chainLength = max_chain;
591			int tempNiceLength = niceLength;
592			short[] tempPrev = prev;
593			int scan = strstart;
594			int match;
595			int best_end = strstart + matchLen;
596			int best_len = Math.Max(matchLen, MinMatch - 1);
597
598			int limit = Math.Max(strstart - MaxDistance, 0);
599
600			int strend = strstart + MaxMatch - 1;
601			byte scan_end1 = window[best_end - 1];
602			byte scan_end = window[best_end];
603
604			// Do not waste too much time if we already have a good match:
605			if (best_len >= goodLength)
606			{
607				chainLength >>= 2;
608			}
609
610			// Do not look for matches beyond the end of the input.
611			// This is necessary to make deflate deterministic.
612			if (tempNiceLength > lookahead)
613			{
614				tempNiceLength = lookahead;
615			}
616
617			do
618			{
619				if (window[curMatch + best_len] != scan_end ||
620				    window[curMatch + best_len - 1] != scan_end1 ||
621				    window[curMatch] != window[scan] ||
622				    window[curMatch + 1] != window[scan + 1])
623				{
624					continue;
625				}
626
627				match = curMatch + 2;
628				scan += 2;
629
630				// We check for insufficient lookahead only every 8th comparison;
631				// the 256th check will be made at strstart + 258.
632				while (window[++scan] == window[++match] &&
633				       window[++scan] == window[++match] &&
634				       window[++scan] == window[++match] &&
635				       window[++scan] == window[++match] &&
636				       window[++scan] == window[++match] &&
637				       window[++scan] == window[++match] &&
638				       window[++scan] == window[++match] &&
639				       window[++scan] == window[++match] &&
640				       scan < strend)
641				{
642					;
643				}
644
645				if (scan > best_end)
646				{
647					matchStart = curMatch;
648					best_end = scan;
649					best_len = scan - strstart;
650
651					if (best_len >= tempNiceLength)
652					{
653						break;
654					}
655
656					scan_end1 = window[best_end - 1];
657					scan_end = window[best_end];
658				}
659				scan = strstart;
660			} while ((curMatch = (tempPrev[curMatch & WMask] & 0xffff)) > limit &&
661			         --chainLength != 0);
662
663			matchLen = Math.Min(best_len, lookahead);
664			return matchLen >= MinMatch;
665		}
666		#endregion
667
668		#region DeflateStored
669		/// <summary>
670		/// Deflate stored
671		/// </summary>
672		/// <param name="flush">Flush</param>
673		/// <param name="finish">Finish</param>
674		/// <returns>
675		/// True if storing succeeded, false if nothing was stored.
676		/// </returns>
677		private bool DeflateStored(bool flush, bool finish)
678		{
679			if (!flush && lookahead == 0)
680			{
681				return false;
682			}
683
684			strstart += lookahead;
685			lookahead = 0;
686
687			int storedLen = strstart - blockStart;
688
689			// Block is full or Block may move out of window
690			if ((storedLen >= MaxBlockSize) ||
691			    (blockStart < WSize && storedLen >= MaxDistance) ||
692			    flush)
693			{
694				bool lastBlock = finish;
695				if (storedLen > MaxBlockSize)
696				{
697					storedLen = MaxBlockSize;
698					lastBlock = false;
699				}
700
701				huffman.FlushStoredBlock(window, blockStart, storedLen, lastBlock);
702				blockStart += storedLen;
703				return !lastBlock;
704			}
705			return true;
706		}
707		#endregion
708
709		#region DeflateFast
710		/// <summary>
711		/// Deflate fast
712		/// </summary>
713		/// <param name="flush">Flush</param>
714		/// <param name="finish">Finish</param>
715		/// <returns>
716		/// True if storing succeeded, false if nothing was stored.
717		/// </returns>
718		private bool DeflateFast(bool flush, bool finish)
719		{
720			if (lookahead < MinLookAhead && !flush)
721			{
722				return false;
723			}
724
725			while (lookahead >= MinLookAhead || flush)
726			{
727				if (lookahead == 0)
728				{
729					// We are flushing everything
730					huffman.FlushBlock(window, blockStart, strstart - blockStart, finish);
731					blockStart = strstart;
732					return false;
733				}
734
735				if (strstart > 2 * WSize - MinLookAhead)
736				{
737					// slide window, as findLongestMatch needs this. This should only
738					// happen when flushing and the window is almost full.
739					SlideWindow();
740				}
741
742				int hashHead;
743				if (lookahead >= MinMatch &&
744				    (hashHead = InsertString()) != 0 &&
745				    strategy != DeflateStrategy.HuffmanOnly &&
746				    strstart - hashHead <= MaxDistance &&
747				    FindLongestMatch(hashHead))
748				{
749					// longestMatch sets matchStart and matchLen
750
751					//  This stops problems with fast/low compression and index out of
752					// range
753					if (huffman.TallyDist(strstart - matchStart, matchLen))
754					{
755						bool lastBlock = finish && lookahead == 0;
756						huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);
757						blockStart = strstart;
758					}
759
760					lookahead -= matchLen;
761					if (matchLen <= max_lazy && lookahead >= MinMatch)
762					{
763						while (--matchLen > 0)
764						{
765							++strstart;
766							InsertString();
767						}
768						++strstart;
769					}
770					else
771					{
772						strstart += matchLen;
773						if (lookahead >= MinMatch - 1)
774						{
775							UpdateHash();
776						}
777					}
778					matchLen = MinMatch - 1;
779					continue;
780				}
781				else
782				{
783					/* No match found */
784					huffman.TallyLit(window[strstart] & 0xff);
785					++strstart;
786					--lookahead;
787				}
788
789				if (huffman.IsFull())
790				{
791					bool lastBlock = finish && lookahead == 0;
792					huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);
793					blockStart = strstart;
794					return !lastBlock;
795				}
796			}
797			return true;
798		}
799		#endregion
800
801		#region DeflateSlow
802		/// <summary>
803		/// Deflate slow
804		/// </summary>
805		/// <param name="flush">Flush</param>
806		/// <param name="finish">Finish</param>
807		/// <returns>
808		/// True if storing succeeded, false if nothing was stored.
809		/// </returns>
810		private bool DeflateSlow(bool flush, bool finish)
811		{
812			if (lookahead < MinLookAhead && !flush)
813			{
814				return false;
815			}
816
817			while (lookahead >= MinLookAhead || flush)
818			{
819				if (lookahead == 0)
820				{
821					if (prevAvailable)
822					{
823						huffman.TallyLit(window[strstart - 1] & 0xff);
824					}
825					prevAvailable = false;
826
827					// We are flushing everything
828					huffman.FlushBlock(window, blockStart, strstart - blockStart,
829						finish);
830					blockStart = strstart;
831					return false;
832				}
833
834				if (strstart >= 2 * WSize - MinLookAhead)
835				{
836					// slide window, as findLongestMatch need this. This should only
837					// happen when flushing and the window is almost full.
838					SlideWindow();
839				}
840
841				int prevMatch = matchStart;
842				int prevLen = matchLen;
843				DeflateSlowCheckLookAhead();
844				prevLen = DeflateSlowCheckPreviousMatch(prevMatch, prevLen);
845
846				if (huffman.IsFull())
847				{
848					int len = strstart - blockStart;
849					if (prevAvailable)
850					{
851						len--;
852					}
853					bool lastBlock = (finish && lookahead == 0 && !prevAvailable);
854					huffman.FlushBlock(window, blockStart, len, lastBlock);
855					blockStart += len;
856					return !lastBlock;
857				}
858			}
859
860			return true;
861		}
862		#endregion
863
864		#region DeflateSlowCheckLookAhead
865		/// <summary>
866		/// Deflate slow check look ahead
867		/// </summary>
868		private void DeflateSlowCheckLookAhead()
869		{
870			if (lookahead >= MinMatch)
871			{
872				int hashHead = InsertString();
873				if (strategy != DeflateStrategy.HuffmanOnly &&
874				    hashHead != 0 && strstart - hashHead <= MaxDistance &&
875				    FindLongestMatch(hashHead))
876				{
877					// longestMatch sets matchStart and matchLen
878
879					// Discard match if too small and too far away
880					if (matchLen <= 5 &&
881					    (strategy == DeflateStrategy.Filtered ||
882					     (matchLen == MinMatch && strstart - matchStart > LengthTooFar)))
883					{
884						matchLen = MinMatch - 1;
885					}
886				}
887			}
888		}
889		#endregion
890
891		#region DeflateSlowCheckPreviousMatch
892		/// <summary>
893		/// Deflate slow check previous match
894		/// </summary>
895		/// <param name="prevMatch">Prev match</param>
896		/// <param name="prevLen">Prev len</param>
897		/// <returns>Int</returns>
898		private int DeflateSlowCheckPreviousMatch(int prevMatch, int prevLen)
899		{
900			// previous match was better
901			if (prevLen >= MinMatch &&
902			    matchLen <= prevLen)
903			{
904				huffman.TallyDist(strstart - 1 - prevMatch, prevLen);
905				prevLen -= 2;
906				do
907				{
908					strstart++;
909					lookahead--;
910					if (lookahead >= MinMatch)
911					{
912						InsertString();
913					}
914				} while (--prevLen > 0);
915				strstart++;
916				lookahead--;
917				prevAvailable = false;
918				matchLen = MinMatch - 1;
919			}
920			else
921			{
922				if (prevAvailable)
923				{
924					huffman.TallyLit(window[strstart - 1] & 0xff);
925				}
926				prevAvailable = true;
927				strstart++;
928				lookahead--;
929			}
930
931			return prevLen;
932		}
933		#endregion
934
935		#endregion
936	}
937}