Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / Compression / Streams / DeflaterOutputStream.cs
1 // DeflaterOutputStream.cs
2 //
3 // Copyright (C) 2001 Mike Krueger
4 //
5 // This file was translated from java, it was part of the GNU Classpath
6 // Copyright (C) 2001 Free Software Foundation, Inc.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 //
22 // Linking this library statically or dynamically with other modules is
23 // making a combined work based on this library.  Thus, the terms and
24 // conditions of the GNU General Public License cover the whole
25 // combination.
26 // 
27 // As a special exception, the copyright holders of this library give you
28 // permission to link this library with independent modules to produce an
29 // executable, regardless of the license terms of these independent
30 // modules, and to copy and distribute the resulting executable under
31 // terms of your choice, provided that you also meet, for each linked
32 // independent module, the terms and conditions of the license of that
33 // module.  An independent module is a module which is not derived from
34 // or based on this library.  If you modify this library, you may extend
35 // this exception to your version of the library, but you are not
36 // obligated to do so.  If you do not wish to do so, delete this
37 // exception statement from your version.
38
39 using System;
40 using System.IO;
41 using System.Security.Cryptography;
42 using ICSharpCode.SharpZipLib.Silverlight.Encryption;
43
44 namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression.Streams
45 {
46     /// <summary>
47     /// A special stream deflating or compressing the bytes that are
48     /// written to it.  It uses a Deflater to perform actual deflating.<br/>
49     /// Authors of the original java version : Tom Tromey, Jochen Hoenicke 
50     /// </summary>
51     public class DeflaterOutputStream : Stream
52     {
53         #region Constructors
54
55         /// <summary>
56         /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
57         /// </summary>
58         /// <param name="baseOutputStream">
59         /// the output stream where deflated output should be written.
60         /// </param>
61         public DeflaterOutputStream(Stream baseOutputStream)
62             : this(baseOutputStream, new Deflater(), 512)
63         {
64         }
65
66         /// <summary>
67         /// Creates a new DeflaterOutputStream with the given Deflater and
68         /// default buffer size.
69         /// </summary>
70         /// <param name="baseOutputStream">
71         /// the output stream where deflated output should be written.
72         /// </param>
73         /// <param name="deflater">
74         /// the underlying deflater.
75         /// </param>
76         public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
77             : this(baseOutputStream, deflater, 512)
78         {
79         }
80
81         /// <summary>
82         /// Creates a new DeflaterOutputStream with the given Deflater and
83         /// buffer size.
84         /// </summary>
85         /// <param name="baseOutputStream">
86         /// The output stream where deflated output is written.
87         /// </param>
88         /// <param name="deflater">
89         /// The underlying deflater to use
90         /// </param>
91         /// <param name="bufferSize">
92         /// The buffer size to use when deflating
93         /// </param>
94         /// <exception cref="ArgumentOutOfRangeException">
95         /// bufsize is less than or equal to zero.
96         /// </exception>
97         /// <exception cref="ArgumentException">
98         /// baseOutputStream does not support writing
99         /// </exception>
100         /// <exception cref="ArgumentNullException">
101         /// deflater instance is null
102         /// </exception>
103         public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
104         {
105             if (baseOutputStream == null)
106             {
107                 throw new ArgumentNullException("baseOutputStream");
108             }
109
110             if (baseOutputStream.CanWrite == false)
111             {
112                 throw new ArgumentException("Must support writing", "baseOutputStream");
113             }
114
115             if (deflater == null)
116             {
117                 throw new ArgumentNullException("deflater");
118             }
119
120             if (bufferSize <= 0)
121             {
122                 throw new ArgumentOutOfRangeException("bufferSize");
123             }
124
125             baseOutputStream_ = baseOutputStream;
126             buffer_ = new byte[bufferSize];
127             deflater_ = deflater;
128         }
129
130         #endregion
131
132         #region Public API
133
134         /// <summary>
135         /// Get/set flag indicating ownership of the underlying stream.
136         /// When the flag is true <see cref="Close"></see> will close the underlying stream also.
137         /// </summary>
138         public bool IsStreamOwner
139         {
140             get { return isStreamOwner_; }
141             set { isStreamOwner_ = value; }
142         }
143
144         ///     <summary>
145         /// Allows client to determine if an entry can be patched after its added
146         /// </summary>
147         public bool CanPatchEntries
148         {
149             get { return baseOutputStream_.CanSeek; }
150         }
151
152         /// <summary>
153         /// Finishes the stream by calling finish() on the deflater. 
154         /// </summary>
155         /// <exception cref="SharpZipBaseException">
156         /// Not all input is deflated
157         /// </exception>
158         public virtual void Finish()
159         {
160             deflater_.Finish();
161             while (!deflater_.IsFinished)
162             {
163                 var len = deflater_.Deflate(buffer_, 0, buffer_.Length);
164                 if (len <= 0)
165                 {
166                     break;
167                 }
168                 if (cryptoTransform_ != null)
169                 {
170                     EncryptBlock(buffer_, 0, len);
171                 }
172
173                 baseOutputStream_.Write(buffer_, 0, len);
174             }
175
176             if (!deflater_.IsFinished)
177             {
178                 throw new SharpZipBaseException("Can't deflate all input?");
179             }
180
181             baseOutputStream_.Flush();
182
183             if (cryptoTransform_ == null)
184             {
185                 return;
186             }
187             cryptoTransform_.Dispose();
188             cryptoTransform_ = null;
189         }
190
191         #endregion
192
193         #region Encryption
194
195         private ICryptoTransform cryptoTransform_;
196         private string _password;
197
198         /// <summary>
199         /// Get/set the password used for encryption.
200         /// </summary>
201         /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
202         public string Password
203         {
204             get { return _password; }
205             set
206             {
207                 if ((value != null) && (value.Length == 0))
208                 {
209                     _password = null;
210                 }
211                 else
212                 {
213                     _password = value;
214                 }
215             }
216         }
217
218         /// <summary>
219         /// Encrypt a block of data
220         /// </summary>
221         /// <param name="buffer">
222         /// Data to encrypt.  NOTE the original contents of the buffer are lost
223         /// </param>
224         /// <param name="offset">
225         /// Offset of first byte in buffer to encrypt
226         /// </param>
227         /// <param name="length">
228         /// Number of bytes in buffer to encrypt
229         /// </param>
230         protected void EncryptBlock(byte[] buffer, int offset, int length)
231         {
232             cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
233         }
234
235         /// <summary>
236         /// Initializes encryption keys based on given password
237         /// </summary>
238         /// <param name="password">The password.</param>
239         protected void InitializePassword(string password)
240         {
241             var pkManaged = new PkzipClassicManaged();
242             var key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
243             cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
244         }
245
246         #endregion
247
248         #region Deflation Support
249
250         /// <summary>
251         /// Deflates everything in the input buffers.  This will call
252         /// <code>def.deflate()</code> until all bytes from the input buffers
253         /// are processed.
254         /// </summary>
255         protected void Deflate()
256         {
257             while (!deflater_.IsNeedingInput)
258             {
259                 var deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
260
261                 if (deflateCount <= 0)
262                 {
263                     break;
264                 }
265                 if (cryptoTransform_ != null)
266                 {
267                     EncryptBlock(buffer_, 0, deflateCount);
268                 }
269
270                 baseOutputStream_.Write(buffer_, 0, deflateCount);
271             }
272
273             if (!deflater_.IsNeedingInput)
274             {
275                 throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
276             }
277         }
278
279         #endregion
280
281         #region Stream Overrides
282
283         /// <summary>
284         /// Gets value indicating stream can be read from
285         /// </summary>
286         public override bool CanRead
287         {
288             get { return false; }
289         }
290
291         /// <summary>
292         /// Gets a value indicating if seeking is supported for this stream
293         /// This property always returns false
294         /// </summary>
295         public override bool CanSeek
296         {
297             get { return false; }
298         }
299
300         /// <summary>
301         /// Get value indicating if this stream supports writing
302         /// </summary>
303         public override bool CanWrite
304         {
305             get { return baseOutputStream_.CanWrite; }
306         }
307
308         /// <summary>
309         /// Get current length of stream
310         /// </summary>
311         public override long Length
312         {
313             get { return baseOutputStream_.Length; }
314         }
315
316         /// <summary>
317         /// Gets the current position within the stream.
318         /// </summary>
319         /// <exception cref="NotSupportedException">Any attempt to set position</exception>
320         public override long Position
321         {
322             get { return baseOutputStream_.Position; }
323             set { throw new NotSupportedException("Position property not supported"); }
324         }
325
326         /// <summary>
327         /// Sets the current position of this stream to the given value. Not supported by this class!
328         /// </summary>
329         /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
330         /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
331         /// <returns>The new position in the stream.</returns>
332         /// <exception cref="NotSupportedException">Any access</exception>
333         public override long Seek(long offset, SeekOrigin origin)
334         {
335             throw new NotSupportedException("DeflaterOutputStream Seek not supported");
336         }
337
338         /// <summary>
339         /// Sets the length of this stream to the given value. Not supported by this class!
340         /// </summary>
341         /// <param name="value">The new stream length.</param>
342         /// <exception cref="NotSupportedException">Any access</exception>
343         public override void SetLength(long value)
344         {
345             throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
346         }
347
348         /// <summary>
349         /// Read a byte from stream advancing position by one
350         /// </summary>
351         /// <returns>The byte read cast to an int.  THe value is -1 if at the end of the stream.</returns>
352         /// <exception cref="NotSupportedException">Any access</exception>
353         public override int ReadByte()
354         {
355             throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
356         }
357
358         /// <summary>
359         /// Read a block of bytes from stream
360         /// </summary>
361         /// <param name="buffer">The buffer to store read data in.</param>
362         /// <param name="offset">The offset to start storing at.</param>
363         /// <param name="count">The maximum number of bytes to read.</param>
364         /// <returns>The actual number of bytes read.  Zero if end of stream is detected.</returns>
365         /// <exception cref="NotSupportedException">Any access</exception>
366         public override int Read(byte[] buffer, int offset, int count)
367         {
368             throw new NotSupportedException("DeflaterOutputStream Read not supported");
369         }
370
371         /// <summary>
372         /// Asynchronous reads are not supported a NotSupportedException is always thrown
373         /// </summary>
374         /// <param name="buffer">The buffer to read into.</param>
375         /// <param name="offset">The offset to start storing data at.</param>
376         /// <param name="count">The number of bytes to read</param>
377         /// <param name="callback">The async callback to use.</param>
378         /// <param name="state">The state to use.</param>
379         /// <returns>Returns an <see cref="IAsyncResult"/></returns>
380         /// <exception cref="NotSupportedException">Any access</exception>
381         public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback,
382                                                object state)
383         {
384             throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");
385         }
386
387         /// <summary>
388         /// Asynchronous writes arent supported, a NotSupportedException is always thrown
389         /// </summary>
390         /// <param name="buffer">The buffer to write.</param>
391         /// <param name="offset">The offset to begin writing at.</param>
392         /// <param name="count">The number of bytes to write.</param>
393         /// <param name="callback">The <see cref="AsyncCallback"/> to use.</param>
394         /// <param name="state">The state object.</param>
395         /// <returns>Returns an IAsyncResult.</returns>
396         /// <exception cref="NotSupportedException">Any access</exception>
397         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback,
398                                                 object state)
399         {
400             throw new NotSupportedException("BeginWrite is not supported");
401         }
402
403         /// <summary>
404         /// Flushes the stream by calling <see cref="DeflaterOutputStream.Flush">Flush</see> on the deflater and then
405         /// on the underlying stream.  This ensures that all bytes are flushed.
406         /// </summary>
407         public override void Flush()
408         {
409             deflater_.Flush();
410             Deflate();
411             baseOutputStream_.Flush();
412         }
413
414         /// <summary>
415         /// Calls <see cref="Finish"/> and closes the underlying
416         /// stream when <see cref="IsStreamOwner"></see> is true.
417         /// </summary>
418         public override void Close()
419         {
420             if (!isClosed_)
421             {
422                 isClosed_ = true;
423
424                 try
425                 {
426                     Finish();
427                     if (cryptoTransform_ != null)
428                     {
429                         cryptoTransform_.Dispose();
430                         cryptoTransform_ = null;
431                     }
432                 }
433                 finally
434                 {
435                     if (isStreamOwner_)
436                     {
437                         baseOutputStream_.Close();
438                     }
439                 }
440             }
441         }
442
443         /// <summary>
444         /// Writes a single byte to the compressed output stream.
445         /// </summary>
446         /// <param name="value">
447         /// The byte value.
448         /// </param>
449         public override void WriteByte(byte value)
450         {
451             var b = new byte[1];
452             b[0] = value;
453             Write(b, 0, 1);
454         }
455
456         /// <summary>
457         /// Writes bytes from an array to the compressed stream.
458         /// </summary>
459         /// <param name="buffer">
460         /// The byte array
461         /// </param>
462         /// <param name="offset">
463         /// The offset into the byte array where to start.
464         /// </param>
465         /// <param name="count">
466         /// The number of bytes to write.
467         /// </param>
468         public override void Write(byte[] buffer, int offset, int count)
469         {
470             deflater_.SetInput(buffer, offset, count);
471             Deflate();
472         }
473
474         #endregion
475
476         #region Instance Fields
477
478         /// <summary>
479         /// This buffer is used temporarily to retrieve the bytes from the
480         /// deflater and write them to the underlying output stream.
481         /// </summary>
482         private readonly byte[] buffer_;
483
484         /// <summary>
485         /// Base stream the deflater depends on.
486         /// </summary>
487         protected Stream baseOutputStream_;
488
489         /// <summary>
490         /// The deflater which is used to deflate the stream.
491         /// </summary>
492         protected Deflater deflater_;
493
494         private bool isClosed_;
495
496         private bool isStreamOwner_ = true;
497
498         #endregion
499     }
500 }