PageRenderTime 77ms CodeModel.GetById 40ms app.highlight 17ms RepoModel.GetById 13ms app.codeStats 0ms

/Utilities/Compression/Deflaters/Deflater.cs

#
C# | 655 lines | 283 code | 57 blank | 315 comment | 46 complexity | 2b5ce61d6bec7fd66112f0c1b20b87aa 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.Helpers;
  7
  8namespace Delta.Utilities.Compression.Deflaters
  9{
 10	/// <summary>
 11	/// This is the Deflater class. The deflater class compresses input
 12	/// with the deflate algorithm described in RFC 1951.  It has several
 13	/// compression levels and three different strategies described below.
 14	/// <para />
 15	/// This class is <i>not</i> thread safe. This is inherent in the API, due
 16	/// to the split of deflate and setInput.
 17	/// <para />
 18	/// author of the original java version : Jochen Hoenicke
 19	/// </summary>
 20	public class Deflater
 21	{
 22		#region Constants
 23		/// <summary>
 24		/// The best and slowest compression level.
 25		/// This tries to find very long and distant string repetitions.
 26		/// </summary>
 27		public const int BestCompression = 9;
 28
 29		/// <summary>
 30		/// The worst but fastest compression level.
 31		/// </summary>
 32		public const int BestSpeed = 1;
 33
 34		/// <summary>
 35		/// The default compression level.
 36		/// </summary>
 37		public const int DefaultCompression = MathHelper.InvalidIndex;
 38
 39		/// <summary>
 40		/// This level won't compress at all but output uncompressed blocks.
 41		/// </summary>
 42		public const int NoCompression = 0;
 43
 44		/// <summary>
 45		/// The compression method. This is the only method supported so far.
 46		/// There is no need to use this constant at all.
 47		/// </summary>
 48		public const int Deflated = 8;
 49
 50		/*
 51		* The Deflater can do the following state transitions:
 52		* (1) -> InitializeState   ----> InitializeFinishingState ---.
 53		*        /  | (2)      (5)                         |
 54		*       /   v          (5)                         |
 55		*   (3)| SetDictionaryState ---> SetDictionaryFinishingState |(3)
 56		*       \   | (3)                 |        ,-------'
 57		*        |  |                     | (3)   /
 58		*        v  v          (5)        v      v
 59		* (1) -> BusyState   ----> FinishingState
 60		*                                | (6)
 61		*                                v
 62		*                           FinishedState
 63		*    \_____________________________________/
 64		*          | (7)
 65		*          v
 66		*        ClosedState
 67
 68		* (1) If we should produce a header we start in InitializeState, otherwise
 69		*     we start in BusyState.
 70		* (2) A dictionary may be set only when we are in InitializeState, then
 71		*     we change the state as indicated.
 72		* (3) Whether a dictionary is set or not, on the first call of deflate
 73		*     we change to BusyState.
 74		* (4) -- intentionally left blank -- :)
 75		* (5) FinishingState is entered, when flush() is called to indicate that
 76		*     there is no more INPUT.  There are also states indicating, that
 77		*     the header wasn't written yet.
 78		* (6) FinishedState is entered, when everything has been flushed to the
 79		*     internal pending output buffer.
 80		* (7) At any time (7)
 81		*/
 82
 83		/// <summary>
 84		/// Is set dictionary
 85		/// </summary>
 86		/// <returns>0x01</returns>
 87		private const int IsSetDictionary = 0x01;
 88
 89		/// <summary>
 90		/// Is flushing
 91		/// </summary>
 92		/// <returns>0x04</returns>
 93		private const int IsFlushing = 0x04;
 94
 95		/// <summary>
 96		/// Is finishing
 97		/// </summary>
 98		/// <returns>0x08</returns>
 99		private const int IsFinishing = 0x08;
100
101		/// <summary>
102		/// Initialize state
103		/// </summary>
104		/// <returns>0x00</returns>
105		private const int InitializeState = 0x00;
106
107		/// <summary>
108		/// Set dictionary state
109		/// </summary>
110		/// <returns>0x01</returns>
111		private const int SetDictionaryState = 0x01;
112
113		/*unused
114		/// <summary>
115		/// Initialize finishing state
116		/// </summary>
117		/// <returns>0x08</returns>
118		//private static int InitializeFinishingState    = 0x08;
119		/// <summary>
120		/// Set dictionary finishing state
121		/// </summary>
122		/// <returns>0x09</returns>
123		//private static int SetDictionaryFinishingState = 0x09;
124		*/
125
126		/// <summary>
127		/// Busy state
128		/// </summary>
129		/// <returns>0x10</returns>
130		private const int BusyState = 0x10;
131
132		/// <summary>
133		/// Flushing state
134		/// </summary>
135		/// <returns>0x14</returns>
136		private const int FlushingState = 0x14;
137
138		/// <summary>
139		/// Finishing state
140		/// </summary>
141		/// <returns>0x1c</returns>
142		private const int FinishingState = 0x1c;
143
144		/// <summary>
145		/// Finished state
146		/// </summary>
147		/// <returns>0x 1e</returns>
148		private const int FinishedState = 0x1e;
149
150		/// <summary>
151		/// Closed state
152		/// </summary>
153		/// <returns>0x7f</returns>
154		private const int ClosedState = 0x7f;
155		#endregion
156
157		#region Adler (Public)
158		/// <summary>
159		/// Gets the current adler checksum of the data that was processed so far.
160		/// </summary>
161		public int Adler
162		{
163			get
164			{
165				return engine.Adler;
166			}
167		}
168		#endregion
169
170		#region TotalIn (Public)
171		/// <summary>
172		/// Gets the number of input bytes processed so far.
173		/// </summary>
174		public int TotalIn
175		{
176			get
177			{
178				return engine.TotalIn;
179			}
180		}
181		#endregion
182
183		#region TotalOut (Public)
184		/// <summary>
185		/// Gets the number of output bytes so far.
186		/// </summary>
187		public long TotalOut
188		{
189			get
190			{
191				return totalOut;
192			}
193		}
194		#endregion
195
196		#region IsFinished (Public)
197		/// <summary>
198		/// Returns true if the stream was finished and no more output bytes
199		/// are available.
200		/// </summary>
201		public bool IsFinished
202		{
203			get
204			{
205				return state == FinishedState && pending.IsFlushed;
206			}
207		}
208		#endregion
209
210		#region IsNeedingInput (Public)
211		/// <summary>
212		/// Returns true, if the input buffer is empty.
213		/// You should then call setInput(). 
214		/// NOTE: This method can also return true when the stream
215		/// was finished.
216		/// </summary>
217		public bool IsNeedingInput
218		{
219			get
220			{
221				return engine.NeedsInput();
222			}
223		}
224		#endregion
225
226		#region Private
227
228		#region level (Private)
229		/// <summary>
230		/// Compression level.
231		/// </summary>
232		private int level;
233		#endregion
234
235		#region noZlibHeaderOrFooter (Private)
236		/// <summary>
237		/// If true no Zlib/RFC1950 headers or footers are generated
238		/// </summary>
239		private readonly bool noZlibHeaderOrFooter;
240		#endregion
241
242		#region state (Private)
243		/// <summary>
244		/// The current state.
245		/// </summary>
246		private int state;
247		#endregion
248
249		#region totalOut (Private)
250		/// <summary>
251		/// The total bytes of output written.
252		/// </summary>
253		private long totalOut;
254		#endregion
255
256		#region pending (Private)
257		/// <summary>
258		/// The pending output.
259		/// </summary>
260		private readonly DeflaterPending pending;
261		#endregion
262
263		#region engine (Private)
264		/// <summary>
265		/// The deflater engine.
266		/// </summary>
267		private readonly DeflaterEngine engine;
268		#endregion
269
270		#endregion
271
272		#region Constructors
273		/// <summary>
274		/// Creates a new deflater with default compression level.
275		/// </summary>
276		public Deflater()
277			: this(DefaultCompression, false)
278		{
279		}
280
281		/// <summary>
282		/// Creates a new deflater with given compression level.
283		/// </summary>
284		/// <param name="level">The compression level, a value between
285		/// NoCompression and BestCompression, or DefaultCompression.
286		/// </param>
287		/// <exception cref="System.ArgumentOutOfRangeException">
288		/// If level is out of range.</exception>
289		public Deflater(int level)
290			: this(level, false)
291		{
292		}
293
294		/// <summary>
295		/// Creates a new deflater with given compression level.
296		/// </summary>
297		/// <param name="level">
298		/// the compression level, a value between NoCompression
299		/// and BestCompression.
300		/// </param>
301		/// <param name="noZlibHeaderOrFooter">
302		/// true, if we should suppress the Zlib/RFC1950 header at the
303		/// beginning and the adler checksum at the end of the output.
304		/// This is useful for the GZIP/PKZIP formats.
305		/// </param>
306		/// <exception cref="System.ArgumentOutOfRangeException">
307		/// If level is out of range.</exception>
308		public Deflater(int level, bool noZlibHeaderOrFooter)
309		{
310			if (level == DefaultCompression)
311			{
312				level = 6;
313			}
314			else if (level < NoCompression ||
315			         level > BestCompression)
316			{
317				throw new ArgumentOutOfRangeException("level");
318			}
319
320			pending = new DeflaterPending();
321			engine = new DeflaterEngine(pending);
322			this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
323			SetStrategy(DeflateStrategy.Default);
324			SetLevel(level);
325			Reset();
326		}
327		#endregion
328
329		#region Reset (Public)
330		/// <summary>
331		/// Resets the deflater.  The deflater acts afterwards as if it was
332		/// just created with the same compression level and strategy as it
333		/// had before.
334		/// </summary>
335		public void Reset()
336		{
337			state = (noZlibHeaderOrFooter
338			         	? BusyState
339			         	: InitializeState);
340			totalOut = 0;
341			pending.Reset();
342			engine.Reset();
343		}
344		#endregion
345
346		#region Flush (Public)
347		/// <summary>
348		/// Flushes the current input block.  Further calls to deflate() will
349		/// produce enough output to inflate everything in the current input
350		/// block.  This is not part of Sun's JDK so I have made it package
351		/// private.  It is used by DeflaterOutputStream to implement
352		/// flush().
353		/// </summary>
354		public void Flush()
355		{
356			state |= IsFlushing;
357		}
358		#endregion
359
360		#region Finish (Public)
361		/// <summary>
362		/// Finishes the deflater with the current input block.  It is an error
363		/// to give more input after this method was called. This method must
364		/// be called to force all bytes to be flushed.
365		/// </summary>
366		public void Finish()
367		{
368			state |= IsFlushing | IsFinishing;
369		}
370		#endregion
371
372		#region SetInput (Public)
373		/// <summary>
374		/// Sets the data which should be compressed next.  This should be only
375		/// called when needsInput indicates that more input is needed.
376		/// If you call setInput when needsInput() returns false, the
377		/// previous input that is still pending will be thrown away.
378		/// The given byte array should not be changed, before needsInput()
379		/// returns true again.
380		/// This call is equivalent to <code>setInput(input, 0, input.length)
381		/// </code>.
382		/// </summary>
383		/// <param name="input">
384		/// the buffer containing the input data.
385		/// </param>
386		/// <exception cref="System.InvalidOperationException">
387		/// if the buffer was finished() or ended().
388		/// </exception>
389		public void SetInput(byte[] input)
390		{
391			SetInput(input, 0, input.Length);
392		}
393
394		/// <summary>
395		/// Sets the data which should be compressed next.  This should be
396		/// only called when needsInput indicates that more input is needed.
397		/// The given byte array should not be changed, before needsInput() returns
398		/// true again.
399		/// </summary>
400		/// <param name="input">The buffer containing the input data.</param>
401		/// <param name="off">The start of the data.</param>
402		/// <param name="len">The length of the data.</param>
403		/// <exception cref="System.InvalidOperationException">
404		/// if the buffer was finished() or ended() or if previous input is still
405		/// pending.
406		/// </exception>
407		public void SetInput(byte[] input, int off, int len)
408		{
409			if ((state & IsFinishing) != 0)
410			{
411				throw new InvalidOperationException("finish()/end() already called");
412			}
413			engine.SetInput(input, off, len);
414		}
415		#endregion
416
417		#region SetLevel (Public)
418		/// <summary>
419		/// Sets the compression level.  There is no guarantee of the exact
420		/// position of the change, but if you call this when needsInput is
421		/// true the change of compression level will occur somewhere near
422		/// before the end of the so far given input.
423		/// </summary>
424		/// <param name="lvl">
425		/// the new compression level.
426		/// </param>
427		public void SetLevel(int lvl)
428		{
429			if (lvl == DefaultCompression)
430			{
431				lvl = 6;
432			}
433			else if (lvl < NoCompression || lvl > BestCompression)
434			{
435				throw new ArgumentOutOfRangeException("lvl");
436			}
437
438			if (level != lvl)
439			{
440				level = lvl;
441				engine.SetLevel(lvl);
442			}
443		}
444		#endregion
445
446		#region GetLevel (Public)
447		/// <summary>
448		/// Get current compression level
449		/// </summary>
450		/// <returns>Returns the current compression level</returns>
451		public int GetLevel()
452		{
453			return level;
454		}
455		#endregion
456
457		#region SetStrategy (Public)
458		/// <summary>
459		/// Sets the compression strategy. Strategy is one of
460		/// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED.  For the exact
461		/// position where the strategy is changed, the same as for
462		/// setLevel() applies.
463		/// </summary>
464		/// <param name="strategy">
465		/// The new compression strategy.
466		/// </param>
467		public void SetStrategy(DeflateStrategy strategy)
468		{
469			engine.Strategy = strategy;
470		}
471		#endregion
472
473		#region Deflate (Public)
474		/// <summary>
475		/// Deflates the current input block with to the given array.
476		/// </summary>
477		/// <param name="output">
478		/// The buffer where compressed data is stored
479		/// </param>
480		/// <returns>
481		/// The number of compressed bytes added to the output, or 0 if either
482		/// needsInput() or finished() returns true or length is zero.
483		/// </returns>
484		public int Deflate(byte[] output)
485		{
486			return Deflate(output, 0, output.Length);
487		}
488
489		/// <summary>
490		/// Deflates the current input block to the given array.
491		/// </summary>
492		/// <param name="output">Buffer to store the compressed data.</param>
493		/// <param name="offset">Offset into the output array.</param>
494		/// <param name="length">The maximum number of bytes that may be stored.
495		/// </param>
496		/// <returns>
497		/// The number of compressed bytes added to the output, or 0 if either
498		/// needsInput() or finished() returns true or length is zero.
499		/// </returns>
500		/// <exception cref="System.InvalidOperationException">
501		/// If end() was previously called.
502		/// </exception>
503		/// <exception cref="System.ArgumentOutOfRangeException">
504		/// If offset and/or length don't match the array length.
505		/// </exception>
506		public int Deflate(byte[] output, int offset, int length)
507		{
508			int origLength = length;
509
510			if (state == ClosedState)
511			{
512				throw new InvalidOperationException("Deflater closed");
513			}
514
515			if (state < BusyState)
516			{
517				/* output header */
518				int header = (Deflated +
519				              ((DeflaterConstants.MaxWBits - 8) << 4)) << 8;
520				int level_flags = (level - 1) >> 1;
521				if (level_flags < 0 || level_flags > 3)
522				{
523					level_flags = 3;
524				}
525				header |= level_flags << 6;
526				if ((state & IsSetDictionary) != 0)
527				{
528					// Dictionary was set
529					header |= DeflaterConstants.PresetDictionary;
530				}
531				header += 31 - (header % 31);
532
533				pending.WriteShortMsb(header);
534				if ((state & IsSetDictionary) != 0)
535				{
536					int chksum = engine.Adler;
537					engine.ResetAdler();
538					pending.WriteShortMsb(chksum >> 16);
539					pending.WriteShortMsb(chksum & 0xffff);
540				}
541
542				state = BusyState | (state & (IsFlushing | IsFinishing));
543			}
544
545			for (;;)
546			{
547				int count = pending.Flush(output, offset, length);
548				offset += count;
549				totalOut += count;
550				length -= count;
551
552				if (length == 0 || state == FinishedState)
553				{
554					break;
555				}
556
557				if (!engine.Deflate((state & IsFlushing) != 0,
558					(state & IsFinishing) != 0))
559				{
560					if (state == BusyState)
561					{
562						/* We need more input now */
563						return origLength - length;
564					}
565					else if (state == FlushingState)
566					{
567						if (level != NoCompression)
568						{
569							// We have to supply some lookahead.  8 bit lookahead
570							// is needed by the zlib inflater, and we must fill
571							// the next byte, so that all bits are flushed.
572							int neededbits = 8 + ((-pending.BitCount) & 7);
573							while (neededbits > 0)
574							{
575								/* write a static tree block consisting solely of
576								 * an EOF:
577								 */
578								pending.WriteBits(2, 10);
579								neededbits -= 10;
580							}
581						}
582						state = BusyState;
583					}
584					else if (state == FinishingState)
585					{
586						pending.AlignToByte();
587
588						// Compressed data is complete.
589						// Write footer information if required.
590						if (!noZlibHeaderOrFooter)
591						{
592							int adler = engine.Adler;
593							pending.WriteShortMsb(adler >> 16);
594							pending.WriteShortMsb(adler & 0xffff);
595						}
596						state = FinishedState;
597					}
598				}
599			}
600			return origLength - length;
601		}
602		#endregion
603
604		#region SetDictionary (Public)
605		/// <summary>
606		/// Sets the dictionary which should be used in the deflate process.
607		/// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)
608		/// </code>.
609		/// </summary>
610		/// <param name="dict">
611		/// the dictionary.
612		/// </param>
613		/// <exception cref="System.InvalidOperationException">
614		/// if setInput () or deflate () were already called or another dictionary
615		/// was already set.
616		/// </exception>
617		public void SetDictionary(byte[] dict)
618		{
619			SetDictionary(dict, 0, dict.Length);
620		}
621
622		/// <summary>
623		/// Sets the dictionary which should be used in the deflate process.
624		/// The dictionary is a byte array containing strings that are
625		/// likely to occur in the data which should be compressed. The
626		/// dictionary is not stored in the compressed output, only a
627		/// checksum. To decompress the output you need to supply the same
628		/// dictionary again.
629		/// </summary>
630		/// <param name="dict">
631		/// The dictionary data
632		/// </param>
633		/// <param name="offset">
634		/// An offset into the dictionary.
635		/// </param>
636		/// <param name="length">
637		/// The length of the dictionary data to use
638		/// </param>
639		/// <exception cref="System.InvalidOperationException">
640		/// If setInput () or deflate () were already called or another
641		/// dictionary was already set.
642		/// </exception>
643		public void SetDictionary(byte[] dict, int offset, int length)
644		{
645			if (state != InitializeState)
646			{
647				throw new InvalidOperationException();
648			}
649
650			state = SetDictionaryState;
651			engine.SetDictionary(dict, offset, length);
652		}
653		#endregion
654	}
655}