1 // DeflaterOutputStream.cs
3 // Copyright (C) 2001 Mike Krueger
5 // This file was translated from java, it was part of the GNU Classpath
6 // Copyright (C) 2001 Free Software Foundation, Inc.
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.
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.
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.
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
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.
41 using System.Security.Cryptography;
42 using ICSharpCode.SharpZipLib.Silverlight.Encryption;
44 namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression.Streams
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
51 public class DeflaterOutputStream : Stream
56 /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
58 /// <param name="baseOutputStream">
59 /// the output stream where deflated output should be written.
61 public DeflaterOutputStream(Stream baseOutputStream)
62 : this(baseOutputStream, new Deflater(), 512)
67 /// Creates a new DeflaterOutputStream with the given Deflater and
68 /// default buffer size.
70 /// <param name="baseOutputStream">
71 /// the output stream where deflated output should be written.
73 /// <param name="deflater">
74 /// the underlying deflater.
76 public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
77 : this(baseOutputStream, deflater, 512)
82 /// Creates a new DeflaterOutputStream with the given Deflater and
85 /// <param name="baseOutputStream">
86 /// The output stream where deflated output is written.
88 /// <param name="deflater">
89 /// The underlying deflater to use
91 /// <param name="bufferSize">
92 /// The buffer size to use when deflating
94 /// <exception cref="ArgumentOutOfRangeException">
95 /// bufsize is less than or equal to zero.
97 /// <exception cref="ArgumentException">
98 /// baseOutputStream does not support writing
100 /// <exception cref="ArgumentNullException">
101 /// deflater instance is null
103 public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
105 if (baseOutputStream == null)
107 throw new ArgumentNullException("baseOutputStream");
110 if (baseOutputStream.CanWrite == false)
112 throw new ArgumentException("Must support writing", "baseOutputStream");
115 if (deflater == null)
117 throw new ArgumentNullException("deflater");
122 throw new ArgumentOutOfRangeException("bufferSize");
125 baseOutputStream_ = baseOutputStream;
126 buffer_ = new byte[bufferSize];
127 deflater_ = deflater;
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.
138 public bool IsStreamOwner
140 get { return isStreamOwner_; }
141 set { isStreamOwner_ = value; }
145 /// Allows client to determine if an entry can be patched after its added
147 public bool CanPatchEntries
149 get { return baseOutputStream_.CanSeek; }
153 /// Finishes the stream by calling finish() on the deflater.
155 /// <exception cref="SharpZipBaseException">
156 /// Not all input is deflated
158 public virtual void Finish()
161 while (!deflater_.IsFinished)
163 var len = deflater_.Deflate(buffer_, 0, buffer_.Length);
168 if (cryptoTransform_ != null)
170 EncryptBlock(buffer_, 0, len);
173 baseOutputStream_.Write(buffer_, 0, len);
176 if (!deflater_.IsFinished)
178 throw new SharpZipBaseException("Can't deflate all input?");
181 baseOutputStream_.Flush();
183 if (cryptoTransform_ == null)
187 cryptoTransform_.Dispose();
188 cryptoTransform_ = null;
195 private ICryptoTransform cryptoTransform_;
196 private string _password;
199 /// Get/set the password used for encryption.
201 /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
202 public string Password
204 get { return _password; }
207 if ((value != null) && (value.Length == 0))
219 /// Encrypt a block of data
221 /// <param name="buffer">
222 /// Data to encrypt. NOTE the original contents of the buffer are lost
224 /// <param name="offset">
225 /// Offset of first byte in buffer to encrypt
227 /// <param name="length">
228 /// Number of bytes in buffer to encrypt
230 protected void EncryptBlock(byte[] buffer, int offset, int length)
232 cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
236 /// Initializes encryption keys based on given password
238 /// <param name="password">The password.</param>
239 protected void InitializePassword(string password)
241 var pkManaged = new PkzipClassicManaged();
242 var key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
243 cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
248 #region Deflation Support
251 /// Deflates everything in the input buffers. This will call
252 /// <code>def.deflate()</code> until all bytes from the input buffers
255 protected void Deflate()
257 while (!deflater_.IsNeedingInput)
259 var deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
261 if (deflateCount <= 0)
265 if (cryptoTransform_ != null)
267 EncryptBlock(buffer_, 0, deflateCount);
270 baseOutputStream_.Write(buffer_, 0, deflateCount);
273 if (!deflater_.IsNeedingInput)
275 throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
281 #region Stream Overrides
284 /// Gets value indicating stream can be read from
286 public override bool CanRead
288 get { return false; }
292 /// Gets a value indicating if seeking is supported for this stream
293 /// This property always returns false
295 public override bool CanSeek
297 get { return false; }
301 /// Get value indicating if this stream supports writing
303 public override bool CanWrite
305 get { return baseOutputStream_.CanWrite; }
309 /// Get current length of stream
311 public override long Length
313 get { return baseOutputStream_.Length; }
317 /// Gets the current position within the stream.
319 /// <exception cref="NotSupportedException">Any attempt to set position</exception>
320 public override long Position
322 get { return baseOutputStream_.Position; }
323 set { throw new NotSupportedException("Position property not supported"); }
327 /// Sets the current position of this stream to the given value. Not supported by this class!
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)
335 throw new NotSupportedException("DeflaterOutputStream Seek not supported");
339 /// Sets the length of this stream to the given value. Not supported by this class!
341 /// <param name="value">The new stream length.</param>
342 /// <exception cref="NotSupportedException">Any access</exception>
343 public override void SetLength(long value)
345 throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
349 /// Read a byte from stream advancing position by one
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()
355 throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
359 /// Read a block of bytes from stream
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)
368 throw new NotSupportedException("DeflaterOutputStream Read not supported");
372 /// Asynchronous reads are not supported a NotSupportedException is always thrown
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,
384 throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");
388 /// Asynchronous writes arent supported, a NotSupportedException is always thrown
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,
400 throw new NotSupportedException("BeginWrite is not supported");
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.
407 public override void Flush()
411 baseOutputStream_.Flush();
415 /// Calls <see cref="Finish"/> and closes the underlying
416 /// stream when <see cref="IsStreamOwner"></see> is true.
418 public override void Close()
427 if (cryptoTransform_ != null)
429 cryptoTransform_.Dispose();
430 cryptoTransform_ = null;
437 baseOutputStream_.Close();
444 /// Writes a single byte to the compressed output stream.
446 /// <param name="value">
449 public override void WriteByte(byte value)
457 /// Writes bytes from an array to the compressed stream.
459 /// <param name="buffer">
462 /// <param name="offset">
463 /// The offset into the byte array where to start.
465 /// <param name="count">
466 /// The number of bytes to write.
468 public override void Write(byte[] buffer, int offset, int count)
470 deflater_.SetInput(buffer, offset, count);
476 #region Instance Fields
479 /// This buffer is used temporarily to retrieve the bytes from the
480 /// deflater and write them to the underlying output stream.
482 private readonly byte[] buffer_;
485 /// Base stream the deflater depends on.
487 protected Stream baseOutputStream_;
490 /// The deflater which is used to deflate the stream.
492 protected Deflater deflater_;
494 private bool isClosed_;
496 private bool isStreamOwner_ = true;