// DeflaterOutputStream.cs // // Copyright (C) 2001 Mike Krueger // // This file was translated from java, it was part of the GNU Classpath // Copyright (C) 2001 Free Software Foundation, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give you // permission to link this library with independent modules to produce an // executable, regardless of the license terms of these independent // modules, and to copy and distribute the resulting executable under // terms of your choice, provided that you also meet, for each linked // independent module, the terms and conditions of the license of that // module. An independent module is a module which is not derived from // or based on this library. If you modify this library, you may extend // this exception to your version of the library, but you are not // obligated to do so. If you do not wish to do so, delete this // exception statement from your version. using System; using System.IO; using ICSharpCode.SharpZipLib.Checksums; using ICSharpCode.SharpZipLib.Zip.Compression; namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams { /// /// A special stream deflating or compressing the bytes that are /// written to it. It uses a Deflater to perform actual deflating.
/// Authors of the original java version : Tom Tromey, Jochen Hoenicke ///
public class DeflaterOutputStream : Stream { /// /// This buffer is used temporarily to retrieve the bytes from the /// deflater and write them to the underlying output stream. /// protected byte[] buf; /// /// The deflater which is used to deflate the stream. /// protected Deflater def; /// /// Base stream the deflater depends on. /// protected Stream baseOutputStream; bool isClosed = false; bool isStreamOwner = true; /// /// Get/set flag indicating ownership of underlying stream. /// When the flag is true will close the underlying stream also. /// public bool IsStreamOwner { get { return isStreamOwner; } set { isStreamOwner = value; } } /// /// Allows client to determine if an entry can be patched after its added /// public bool CanPatchEntries { get { return baseOutputStream.CanSeek; } } /// /// Gets value indicating stream can be read from /// public override bool CanRead { get { return baseOutputStream.CanRead; } } /// /// Gets a value indicating if seeking is supported for this stream /// This property always returns false /// public override bool CanSeek { get { return false; } } /// /// Get value indicating if this stream supports writing /// public override bool CanWrite { get { return baseOutputStream.CanWrite; } } /// /// Get current length of stream /// public override long Length { get { return baseOutputStream.Length; } } /// /// The current position within the stream. /// Always throws a NotSupportedExceptionNotSupportedException /// /// Any attempt to set position public override long Position { get { return baseOutputStream.Position; } set { throw new NotSupportedException("DefalterOutputStream Position not supported"); } } /// /// Sets the current position of this stream to the given value. Not supported by this class! /// /// Any access public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("DeflaterOutputStream Seek not supported"); } /// /// Sets the length of this stream to the given value. Not supported by this class! /// /// Any access public override void SetLength(long val) { throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); } /// /// Read a byte from stream advancing position by one /// /// Any access public override int ReadByte() { throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); } /// /// Read a block of bytes from stream /// /// Any access public override int Read(byte[] b, int off, int len) { throw new NotSupportedException("DeflaterOutputStream Read not supported"); } /// /// Asynchronous reads are not supported a NotSupportedException is always thrown /// /// /// /// /// /// /// /// Any access public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported"); } /// /// Asynchronous writes arent supported, a NotSupportedException is always thrown /// /// /// /// /// /// /// /// Any access public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("DeflaterOutputStream BeginWrite not currently supported"); } /// /// Deflates everything in the input buffers. This will call /// def.deflate() until all bytes from the input buffers /// are processed. /// protected void Deflate() { while (!def.IsNeedingInput) { int len = def.Deflate(buf, 0, buf.Length); if (len <= 0) { break; } if (this.keys != null) { this.EncryptBlock(buf, 0, len); } baseOutputStream.Write(buf, 0, len); } if (!def.IsNeedingInput) { throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); } } /// /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. /// /// /// the output stream where deflated output should be written. /// public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512) { } /// /// Creates a new DeflaterOutputStream with the given Deflater and /// default buffer size. /// /// /// the output stream where deflated output should be written. /// /// /// the underlying deflater. /// public DeflaterOutputStream(Stream baseOutputStream, Deflater defl) : this(baseOutputStream, defl, 512) { } /// /// Creates a new DeflaterOutputStream with the given Deflater and /// buffer size. /// /// /// The output stream where deflated output is written. /// /// /// The underlying deflater to use /// /// /// The buffer size to use when deflating /// /// /// bufsize is less than or equal to zero. /// /// /// baseOutputStream does not support writing /// /// /// deflater instance is null /// public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufsize) { if (baseOutputStream.CanWrite == false) { throw new ArgumentException("baseOutputStream", "must support writing"); } if (deflater == null) { throw new ArgumentNullException("deflater"); } if (bufsize <= 0) { throw new ArgumentOutOfRangeException("bufsize"); } this.baseOutputStream = baseOutputStream; buf = new byte[bufsize]; def = deflater; } /// /// Flushes the stream by calling flush() on the deflater and then /// on the underlying stream. This ensures that all bytes are /// flushed. /// public override void Flush() { def.Flush(); Deflate(); baseOutputStream.Flush(); } /// /// Finishes the stream by calling finish() on the deflater. /// /// /// Not all input is deflated /// public virtual void Finish() { def.Finish(); while (!def.IsFinished) { int len = def.Deflate(buf, 0, buf.Length); if (len <= 0) { break; } if (this.keys != null) { this.EncryptBlock(buf, 0, len); } baseOutputStream.Write(buf, 0, len); } if (!def.IsFinished) { throw new SharpZipBaseException("Can't deflate all input?"); } baseOutputStream.Flush(); keys = null; } /// /// Calls finish() and closes the underlying /// stream when is true. /// public override void Close() { if ( !isClosed ) { isClosed = true; Finish(); if ( isStreamOwner ) { baseOutputStream.Close(); } } } /// /// Writes a single byte to the compressed output stream. /// /// /// The byte value. /// public override void WriteByte(byte bval) { byte[] b = new byte[1]; b[0] = bval; Write(b, 0, 1); } /// /// Writes bytes from an array to the compressed stream. /// /// /// The byte array /// /// /// The offset into the byte array where to start. /// /// /// The number of bytes to write. /// public override void Write(byte[] buf, int off, int len) { def.SetInput(buf, off, len); Deflate(); } #region Encryption // TODO: Refactor this code. The presence of Zip specific code in this low level class is wrong string password = null; uint[] keys = null; /// /// Get/set the password used for encryption. When null no encryption is performed /// public string Password { get { return password; } set { if ( value != null && value.Length == 0 ) { password = null; } else { password = value; } } } /// /// Encrypt a single byte /// /// /// The encrypted value /// protected byte EncryptByte() { uint temp = ((keys[2] & 0xFFFF) | 2); return (byte)((temp * (temp ^ 1)) >> 8); } /// /// Encrypt a block of data /// /// /// Data to encrypt. NOTE the original contents of the buffer are lost /// /// /// Offset of first byte in buffer to encrypt /// /// /// Number of bytes in buffer to encrypt /// protected void EncryptBlock(byte[] buffer, int offset, int length) { // TODO: refactor to use crypto transform for (int i = offset; i < offset + length; ++i) { byte oldbyte = buffer[i]; buffer[i] ^= EncryptByte(); UpdateKeys(oldbyte); } } /// /// Initializes encryption keys based on given password /// protected void InitializePassword(string password) { keys = new uint[] { 0x12345678, 0x23456789, 0x34567890 }; for (int i = 0; i < password.Length; ++i) { UpdateKeys((byte)password[i]); } } /// /// Update encryption keys /// protected void UpdateKeys(byte ch) { keys[0] = Crc32.ComputeCrc32(keys[0], ch); keys[1] = keys[1] + (byte)keys[0]; keys[1] = keys[1] * 134775813 + 1; keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); } #endregion } }