PageRenderTime 46ms CodeModel.GetById 26ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

/Utilities/Compression/Streams/DeflaterOutputStream.cs

#
C# | 586 lines | 333 code | 51 blank | 202 comment | 26 complexity | 0a3ef4efa0bcf319e00336bc82e719cd 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 System.IO;
  7using Delta.Utilities.Compression.Checksums;
  8using Delta.Utilities.Compression.Deflaters;
  9
 10namespace Delta.Utilities.Compression.Streams
 11{
 12	/// <summary>
 13	/// A special stream deflating or compressing the bytes that are
 14	/// written to it. It uses a Deflater to perform actual deflating.<br/>
 15	/// Authors of the original java version: Tom Tromey, Jochen Hoenicke 
 16	/// </summary>
 17	public class DeflaterOutputStream : Stream
 18	{
 19		#region Length (Public)
 20		/// <summary>
 21		/// Get current length of stream.
 22		/// </summary>
 23		public override long Length
 24		{
 25			get
 26			{
 27				return baseOutputStream.Length;
 28			} // get
 29		}
 30		#endregion
 31
 32		#region Position (Public)
 33		/// <summary>
 34		/// The current position within the stream. If trying to change position,
 35		/// this property throws a NotSupportedExceptionNotSupportedException.
 36		/// </summary>
 37		/// <exception cref="NotSupportedException">
 38		/// Any attempt to set position
 39		/// </exception>
 40		public override long Position
 41		{
 42			get
 43			{
 44				return baseOutputStream.Position;
 45			} // get
 46			set
 47			{
 48				throw new NotSupportedException("DeflaterOutputStream Position" +
 49				                                " not supported");
 50			} // set
 51		}
 52		#endregion
 53
 54		#region CanRead (Public)
 55		/// <summary>
 56		/// Gets value indicating stream can be read from
 57		/// </summary>
 58		public override bool CanRead
 59		{
 60			get
 61			{
 62				return baseOutputStream.CanRead;
 63			} // get
 64		}
 65		#endregion
 66
 67		#region CanSeek (Public)
 68		/// <summary>
 69		/// Gets a value indicating if seeking is supported for this stream.
 70		/// This property always returns false
 71		/// </summary>
 72		public override bool CanSeek
 73		{
 74			get
 75			{
 76				return false;
 77			} // get
 78		}
 79		#endregion
 80
 81		#region CanWrite (Public)
 82		/// <summary>
 83		/// Get value indicating if this stream supports writing.
 84		/// </summary>
 85		public override bool CanWrite
 86		{
 87			get
 88			{
 89				return baseOutputStream.CanWrite;
 90			} // get
 91		}
 92		#endregion
 93
 94		#region IsStreamOwner (Public)
 95		/// <summary>
 96		/// Get/set flag indicating ownership of underlying stream.
 97		/// When the flag is true <see cref="Close"></see> will close the
 98		/// underlying stream also.
 99		/// </summary>
100		public bool IsStreamOwner
101		{
102			get
103			{
104				return isStreamOwner;
105			} // get
106			set
107			{
108				isStreamOwner = value;
109			} // set
110		}
111		#endregion
112
113		#region CanPatchEntries (Public)
114		///	<summary>
115		/// Allows client to determine if an entry can be patched after its added.
116		/// </summary>
117		public bool CanPatchEntries
118		{
119			get
120			{
121				return baseOutputStream.CanSeek;
122			} // get
123		}
124		#endregion
125
126		#region Password (Public)
127		/// <summary>
128		/// Get/set the password used for encryption.
129		/// When null no encryption is performed.
130		/// </summary>
131		public string Password
132		{
133			get
134			{
135				return password;
136			}
137			set
138			{
139				if (value != null &&
140				    value.Length == 0)
141				{
142					password = null;
143				}
144				else
145				{
146					password = value;
147				}
148			}
149		}
150		#endregion
151
152		#region Protected
153
154		#region buffer (Protected)
155		/// <summary>
156		/// This buffer is used temporarily to retrieve the bytes from the
157		/// deflater and write them to the underlying output stream.
158		/// </summary>
159		protected byte[] buffer;
160		#endregion
161
162		#region deflater (Protected)
163		/// <summary>
164		/// The deflater which is used to deflate the stream.
165		/// </summary>
166		protected Deflater deflater;
167		#endregion
168
169		#region baseOutputStream (Protected)
170		/// <summary>
171		/// Base stream the deflater depends on.
172		/// </summary>
173		protected Stream baseOutputStream;
174		#endregion
175
176		#endregion
177
178		#region Private
179
180		#region isClosed (Private)
181		/// <summary>
182		/// Is closed
183		/// </summary>
184		/// <returns>False</returns>
185		private bool isClosed;
186		#endregion
187
188		#region isStreamOwner (Private)
189		/// <summary>
190		/// Is stream owner
191		/// </summary>
192		/// <returns>True</returns>
193		private bool isStreamOwner = true;
194		#endregion
195
196		#region password (Private)
197		/// <summary>
198		/// Note: Refactor this code: The presence of Zip specific code in this
199		/// low level class is not good.
200		/// </summary>
201		private string password;
202		#endregion
203
204		#region keys (Private)
205		/// <summary>
206		/// Keys
207		/// </summary>
208		/// <returns>Null</returns>
209		private uint[] keys;
210		#endregion
211
212		#endregion
213
214		#region Constructors
215		/// <summary>
216		/// Creates a new DeflaterOutputStream with a default Deflater and default
217		/// buffer size.
218		/// </summary>
219		/// <param name="setBaseOutputStream">
220		/// the output stream where deflated output should be written.
221		/// </param>
222		public DeflaterOutputStream(Stream setBaseOutputStream)
223			: this(setBaseOutputStream, new Deflater(), 512)
224		{
225		}
226
227		/// <summary>
228		/// Creates a new DeflaterOutputStream with the given Deflater and
229		/// default buffer size.
230		/// </summary>
231		/// <param name="setBaseOutputStream">
232		/// the output stream where deflated output should be written.
233		/// </param>
234		/// <param name="setDeflater">
235		/// the underlying deflater.
236		/// </param>
237		public DeflaterOutputStream(Stream setBaseOutputStream,
238			Deflater setDeflater)
239			: this(setBaseOutputStream, setDeflater, 512)
240		{
241			deflater = setDeflater;
242		}
243
244		/// <summary>
245		/// Creates a new DeflaterOutputStream with the given Deflater and
246		/// buffer size.
247		/// </summary>
248		/// <param name="baseOutputStream">
249		/// The output stream where deflated output is written.
250		/// </param>
251		/// <param name="deflater">
252		/// The underlying deflater to use
253		/// </param>
254		/// <param name="bufsize">
255		/// The buffer size to use when deflating
256		/// </param>
257		/// <exception cref="ArgumentOutOfRangeException">
258		/// bufsize is less than or equal to zero.
259		/// </exception>
260		/// <exception cref="ArgumentException">
261		/// baseOutputStream does not support writing
262		/// </exception>
263		/// <exception cref="ArgumentNullException">
264		/// deflater instance is null
265		/// </exception>
266		public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater,
267			int bufsize)
268		{
269			if (baseOutputStream.CanWrite == false)
270			{
271				throw new ArgumentException("baseOutputStream", "must support writing");
272			}
273
274			if (deflater == null)
275			{
276				throw new ArgumentNullException("deflater");
277			}
278
279			if (bufsize <= 0)
280			{
281				throw new ArgumentOutOfRangeException("bufsize");
282			}
283
284			this.baseOutputStream = baseOutputStream;
285			buffer = new byte[bufsize];
286			//does nothing: deflater = deflater;
287		}
288		#endregion
289
290		#region Seek (Public)
291		/// <summary>
292		/// Sets the current position of this stream to the given value.
293		/// Not supported by this class!
294		/// </summary>
295		/// <exception cref="NotSupportedException">Any access</exception>
296		public override long Seek(long offset, SeekOrigin origin)
297		{
298			throw new NotSupportedException(
299				"DeflaterOutputStream Seek not supported");
300		}
301		#endregion
302
303		#region SetLength (Public)
304		/// <summary>
305		/// Sets the length of this stream to the given value.
306		/// Not supported by this class!
307		/// </summary>
308		/// <exception cref="NotSupportedException">Any access</exception>
309		public override void SetLength(long value)
310		{
311			throw new NotSupportedException(
312				"DeflaterOutputStream SetLength not supported");
313		}
314		#endregion
315
316		#region ReadByte (Public)
317		/// <summary>
318		/// Read a byte from stream advancing position by one
319		/// </summary>
320		/// <exception cref="NotSupportedException">Any access</exception>
321		public override int ReadByte()
322		{
323			throw new NotSupportedException(
324				"DeflaterOutputStream ReadByte not supported");
325		}
326		#endregion
327
328		#region Read (Public)
329		/// <summary>
330		/// Read a block of bytes from stream
331		/// </summary>
332		/// <exception cref="NotSupportedException">Any access</exception>
333		public override int Read(byte[] buffer, int offset, int count)
334		{
335			throw new NotSupportedException(
336				"DeflaterOutputStream Read not supported");
337		}
338		#endregion
339
340		#region BeginRead (Public)
341		/// <summary>
342		/// Asynchronous reads are not supported a NotSupportedException is
343		/// always thrown
344		/// </summary>
345		/// <param name="buffer"></param>
346		/// <param name="offset"></param>
347		/// <param name="count"></param>
348		/// <param name="callback"></param>
349		/// <param name="state"></param>
350		/// <returns></returns>
351		/// <exception cref="NotSupportedException">Any access</exception>
352		public override IAsyncResult BeginRead(
353			byte[] buffer, int offset, int count,
354			AsyncCallback callback, object state)
355		{
356			throw new NotSupportedException(
357				"DeflaterOutputStream BeginRead not currently supported");
358		}
359		#endregion
360
361		#region BeginWrite (Public)
362		/// <summary>
363		/// Asynchronous writes arent supported, a NotSupportedException is always
364		/// thrown.
365		/// </summary>
366		/// <param name="buffer"></param>
367		/// <param name="offset"></param>
368		/// <param name="count"></param>
369		/// <param name="callback"></param>
370		/// <param name="state"></param>
371		/// <returns></returns>
372		/// <exception cref="NotSupportedException">Any access</exception>
373		public override IAsyncResult BeginWrite(
374			byte[] buffer, int offset, int count,
375			AsyncCallback callback, object state)
376		{
377			throw new NotSupportedException(
378				"DeflaterOutputStream BeginWrite not currently supported");
379		}
380		#endregion
381
382		#region Flush (Public)
383		/// <summary>
384		/// Flushes the stream by calling flush() on the deflater and then
385		/// on the underlying stream.  This ensures that all bytes are
386		/// flushed.
387		/// </summary>
388		public override void Flush()
389		{
390			deflater.Flush();
391			Deflate();
392			baseOutputStream.Flush();
393		}
394		#endregion
395
396		#region Finish (Public)
397		/// <summary>
398		/// Finishes the stream by calling finish() on the deflater. 
399		/// </summary>
400		/// <exception cref="CompressionException">
401		/// Not all input is deflated
402		/// </exception>
403		public virtual void Finish()
404		{
405			deflater.Finish();
406			while (deflater.IsFinished == false)
407			{
408				int len = deflater.Deflate(buffer, 0, buffer.Length);
409				if (len <= 0)
410				{
411					break;
412				}
413
414				if (keys != null)
415				{
416					EncryptBlock(buffer, 0, len);
417				}
418
419				baseOutputStream.Write(buffer, 0, len);
420			}
421			if (!deflater.IsFinished)
422			{
423				throw new CompressionException("Can't deflate all input?");
424			}
425			baseOutputStream.Flush();
426			keys = null;
427		}
428		#endregion
429
430		#region Close (Public)
431		/// <summary>
432		/// Calls finish() and closes the underlying
433		/// stream when <see cref="IsStreamOwner"></see> is true.
434		/// </summary>
435		public override void Close()
436		{
437			if (isClosed == false)
438			{
439				isClosed = true;
440				Finish();
441				if (isStreamOwner)
442				{
443					baseOutputStream.Close();
444				}
445			}
446		}
447		#endregion
448
449		#region WriteByte (Public)
450		/// <summary>
451		/// Writes a single byte to the compressed output stream.
452		/// </summary>
453		/// <param name="value">The byte value.</param>
454		public override void WriteByte(byte value)
455		{
456			byte[] b = new byte[1];
457			b[0] = value;
458			Write(b, 0, 1);
459		}
460		#endregion
461
462		#region Write (Public)
463		/// <summary>
464		/// Writes bytes from an array to the compressed stream.
465		/// </summary>
466		/// <param name="buffer">The byte array</param>
467		/// <param name="offset">The offset into the byte array where to start.
468		/// </param>
469		/// <param name="count">The number of bytes to write.</param>
470		public override void Write(byte[] buffer, int offset, int count)
471		{
472			deflater.SetInput(buffer, offset, count);
473			Deflate();
474		}
475		#endregion
476
477		#region Methods (Private)
478
479		#region Deflate
480		/// <summary>
481		/// Deflates everything in the input buffers. This will call
482		/// <code>def.deflate()</code> until all bytes from the input buffers
483		/// are processed.
484		/// </summary>
485		protected void Deflate()
486		{
487			while (deflater.IsNeedingInput == false)
488			{
489				int len = deflater.Deflate(buffer, 0, buffer.Length);
490
491				if (len <= 0)
492				{
493					break;
494				}
495
496				if (keys != null)
497				{
498					EncryptBlock(buffer, 0, len);
499				}
500
501				baseOutputStream.Write(buffer, 0, len);
502			}
503
504			if (deflater.IsNeedingInput == false)
505			{
506				throw new CompressionException(
507					"DeflaterOutputStream can't deflate all input?");
508			}
509		}
510		#endregion
511
512		#region EncryptByte
513		/// <summary>
514		/// Encrypt a single byte 
515		/// </summary>
516		/// <returns>
517		/// The encrypted value
518		/// </returns>
519		protected byte EncryptByte()
520		{
521			uint temp = ((keys[2] & 0xFFFF) | 2);
522			return (byte)((temp * (temp ^ 1)) >> 8);
523		}
524		#endregion
525
526		#region EncryptBlock
527		/// <summary>
528		/// Encrypt a block of data
529		/// </summary>
530		/// <param name="buffer">
531		/// Data to encrypt.  NOTE the original contents of the buffer are lost
532		/// </param>
533		/// <param name="offset">
534		/// Offset of first byte in buffer to encrypt
535		/// </param>
536		/// <param name="length">
537		/// Number of bytes in buffer to encrypt
538		/// </param>
539		protected void EncryptBlock(byte[] buffer, int offset, int length)
540		{
541			// Note: refactor to use crypto transform
542			for (int i = offset; i < offset + length; ++i)
543			{
544				byte oldbyte = buffer[i];
545				buffer[i] ^= EncryptByte();
546				UpdateKeys(oldbyte);
547			}
548		}
549		#endregion
550
551		#region InitializePassword
552		/// <summary>
553		/// Initializes encryption keys based on given password
554		/// </summary>
555		protected void InitializePassword(string setPassword)
556		{
557			keys = new uint[]
558			{
559				0x12345678,
560				0x23456789,
561				0x34567890
562			};
563
564			for (int i = 0; i < setPassword.Length; ++i)
565			{
566				UpdateKeys((byte)setPassword[i]);
567			}
568		}
569		#endregion
570
571		#region UpdateKeys
572		/// <summary>
573		/// Update encryption keys 
574		/// </summary>		
575		protected void UpdateKeys(byte ch)
576		{
577			keys[0] = Crc32.ComputeCrc32(keys[0], ch);
578			keys[1] = keys[1] + (byte)keys[0];
579			keys[1] = keys[1] * 134775813 + 1;
580			keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
581		}
582		#endregion
583
584		#endregion
585	}
586}