/Utilities/Compression/Streams/DeflaterOutputStream.cs
C# | 586 lines | 333 code | 51 blank | 202 comment | 26 complexity | 0a3ef4efa0bcf319e00336bc82e719cd MD5 | raw file
Possible License(s): Apache-2.0
- // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
- // Authors of the original java version: Jochen Hoenicke, John Leuner
- // See http://www.ISeeSharpCode.com for more information.
-
- using System;
- using System.IO;
- using Delta.Utilities.Compression.Checksums;
- using Delta.Utilities.Compression.Deflaters;
-
- namespace Delta.Utilities.Compression.Streams
- {
- /// <summary>
- /// A special stream deflating or compressing the bytes that are
- /// written to it. It uses a Deflater to perform actual deflating.<br/>
- /// Authors of the original java version: Tom Tromey, Jochen Hoenicke
- /// </summary>
- public class DeflaterOutputStream : Stream
- {
- #region Length (Public)
- /// <summary>
- /// Get current length of stream.
- /// </summary>
- public override long Length
- {
- get
- {
- return baseOutputStream.Length;
- } // get
- }
- #endregion
-
- #region Position (Public)
- /// <summary>
- /// The current position within the stream. If trying to change position,
- /// this property throws a NotSupportedExceptionNotSupportedException.
- /// </summary>
- /// <exception cref="NotSupportedException">
- /// Any attempt to set position
- /// </exception>
- public override long Position
- {
- get
- {
- return baseOutputStream.Position;
- } // get
- set
- {
- throw new NotSupportedException("DeflaterOutputStream Position" +
- " not supported");
- } // set
- }
- #endregion
-
- #region CanRead (Public)
- /// <summary>
- /// Gets value indicating stream can be read from
- /// </summary>
- public override bool CanRead
- {
- get
- {
- return baseOutputStream.CanRead;
- } // get
- }
- #endregion
-
- #region CanSeek (Public)
- /// <summary>
- /// Gets a value indicating if seeking is supported for this stream.
- /// This property always returns false
- /// </summary>
- public override bool CanSeek
- {
- get
- {
- return false;
- } // get
- }
- #endregion
-
- #region CanWrite (Public)
- /// <summary>
- /// Get value indicating if this stream supports writing.
- /// </summary>
- public override bool CanWrite
- {
- get
- {
- return baseOutputStream.CanWrite;
- } // get
- }
- #endregion
-
- #region IsStreamOwner (Public)
- /// <summary>
- /// Get/set flag indicating ownership of underlying stream.
- /// When the flag is true <see cref="Close"></see> will close the
- /// underlying stream also.
- /// </summary>
- public bool IsStreamOwner
- {
- get
- {
- return isStreamOwner;
- } // get
- set
- {
- isStreamOwner = value;
- } // set
- }
- #endregion
-
- #region CanPatchEntries (Public)
- /// <summary>
- /// Allows client to determine if an entry can be patched after its added.
- /// </summary>
- public bool CanPatchEntries
- {
- get
- {
- return baseOutputStream.CanSeek;
- } // get
- }
- #endregion
-
- #region Password (Public)
- /// <summary>
- /// Get/set the password used for encryption.
- /// When null no encryption is performed.
- /// </summary>
- public string Password
- {
- get
- {
- return password;
- }
- set
- {
- if (value != null &&
- value.Length == 0)
- {
- password = null;
- }
- else
- {
- password = value;
- }
- }
- }
- #endregion
-
- #region Protected
-
- #region buffer (Protected)
- /// <summary>
- /// This buffer is used temporarily to retrieve the bytes from the
- /// deflater and write them to the underlying output stream.
- /// </summary>
- protected byte[] buffer;
- #endregion
-
- #region deflater (Protected)
- /// <summary>
- /// The deflater which is used to deflate the stream.
- /// </summary>
- protected Deflater deflater;
- #endregion
-
- #region baseOutputStream (Protected)
- /// <summary>
- /// Base stream the deflater depends on.
- /// </summary>
- protected Stream baseOutputStream;
- #endregion
-
- #endregion
-
- #region Private
-
- #region isClosed (Private)
- /// <summary>
- /// Is closed
- /// </summary>
- /// <returns>False</returns>
- private bool isClosed;
- #endregion
-
- #region isStreamOwner (Private)
- /// <summary>
- /// Is stream owner
- /// </summary>
- /// <returns>True</returns>
- private bool isStreamOwner = true;
- #endregion
-
- #region password (Private)
- /// <summary>
- /// Note: Refactor this code: The presence of Zip specific code in this
- /// low level class is not good.
- /// </summary>
- private string password;
- #endregion
-
- #region keys (Private)
- /// <summary>
- /// Keys
- /// </summary>
- /// <returns>Null</returns>
- private uint[] keys;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Creates a new DeflaterOutputStream with a default Deflater and default
- /// buffer size.
- /// </summary>
- /// <param name="setBaseOutputStream">
- /// the output stream where deflated output should be written.
- /// </param>
- public DeflaterOutputStream(Stream setBaseOutputStream)
- : this(setBaseOutputStream, new Deflater(), 512)
- {
- }
-
- /// <summary>
- /// Creates a new DeflaterOutputStream with the given Deflater and
- /// default buffer size.
- /// </summary>
- /// <param name="setBaseOutputStream">
- /// the output stream where deflated output should be written.
- /// </param>
- /// <param name="setDeflater">
- /// the underlying deflater.
- /// </param>
- public DeflaterOutputStream(Stream setBaseOutputStream,
- Deflater setDeflater)
- : this(setBaseOutputStream, setDeflater, 512)
- {
- deflater = setDeflater;
- }
-
- /// <summary>
- /// Creates a new DeflaterOutputStream with the given Deflater and
- /// buffer size.
- /// </summary>
- /// <param name="baseOutputStream">
- /// The output stream where deflated output is written.
- /// </param>
- /// <param name="deflater">
- /// The underlying deflater to use
- /// </param>
- /// <param name="bufsize">
- /// The buffer size to use when deflating
- /// </param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// bufsize is less than or equal to zero.
- /// </exception>
- /// <exception cref="ArgumentException">
- /// baseOutputStream does not support writing
- /// </exception>
- /// <exception cref="ArgumentNullException">
- /// deflater instance is null
- /// </exception>
- 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;
- buffer = new byte[bufsize];
- //does nothing: deflater = deflater;
- }
- #endregion
-
- #region Seek (Public)
- /// <summary>
- /// Sets the current position of this stream to the given value.
- /// Not supported by this class!
- /// </summary>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException(
- "DeflaterOutputStream Seek not supported");
- }
- #endregion
-
- #region SetLength (Public)
- /// <summary>
- /// Sets the length of this stream to the given value.
- /// Not supported by this class!
- /// </summary>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override void SetLength(long value)
- {
- throw new NotSupportedException(
- "DeflaterOutputStream SetLength not supported");
- }
- #endregion
-
- #region ReadByte (Public)
- /// <summary>
- /// Read a byte from stream advancing position by one
- /// </summary>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override int ReadByte()
- {
- throw new NotSupportedException(
- "DeflaterOutputStream ReadByte not supported");
- }
- #endregion
-
- #region Read (Public)
- /// <summary>
- /// Read a block of bytes from stream
- /// </summary>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override int Read(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException(
- "DeflaterOutputStream Read not supported");
- }
- #endregion
-
- #region BeginRead (Public)
- /// <summary>
- /// Asynchronous reads are not supported a NotSupportedException is
- /// always thrown
- /// </summary>
- /// <param name="buffer"></param>
- /// <param name="offset"></param>
- /// <param name="count"></param>
- /// <param name="callback"></param>
- /// <param name="state"></param>
- /// <returns></returns>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override IAsyncResult BeginRead(
- byte[] buffer, int offset, int count,
- AsyncCallback callback, object state)
- {
- throw new NotSupportedException(
- "DeflaterOutputStream BeginRead not currently supported");
- }
- #endregion
-
- #region BeginWrite (Public)
- /// <summary>
- /// Asynchronous writes arent supported, a NotSupportedException is always
- /// thrown.
- /// </summary>
- /// <param name="buffer"></param>
- /// <param name="offset"></param>
- /// <param name="count"></param>
- /// <param name="callback"></param>
- /// <param name="state"></param>
- /// <returns></returns>
- /// <exception cref="NotSupportedException">Any access</exception>
- public override IAsyncResult BeginWrite(
- byte[] buffer, int offset, int count,
- AsyncCallback callback, object state)
- {
- throw new NotSupportedException(
- "DeflaterOutputStream BeginWrite not currently supported");
- }
- #endregion
-
- #region Flush (Public)
- /// <summary>
- /// Flushes the stream by calling flush() on the deflater and then
- /// on the underlying stream. This ensures that all bytes are
- /// flushed.
- /// </summary>
- public override void Flush()
- {
- deflater.Flush();
- Deflate();
- baseOutputStream.Flush();
- }
- #endregion
-
- #region Finish (Public)
- /// <summary>
- /// Finishes the stream by calling finish() on the deflater.
- /// </summary>
- /// <exception cref="CompressionException">
- /// Not all input is deflated
- /// </exception>
- public virtual void Finish()
- {
- deflater.Finish();
- while (deflater.IsFinished == false)
- {
- int len = deflater.Deflate(buffer, 0, buffer.Length);
- if (len <= 0)
- {
- break;
- }
-
- if (keys != null)
- {
- EncryptBlock(buffer, 0, len);
- }
-
- baseOutputStream.Write(buffer, 0, len);
- }
- if (!deflater.IsFinished)
- {
- throw new CompressionException("Can't deflate all input?");
- }
- baseOutputStream.Flush();
- keys = null;
- }
- #endregion
-
- #region Close (Public)
- /// <summary>
- /// Calls finish() and closes the underlying
- /// stream when <see cref="IsStreamOwner"></see> is true.
- /// </summary>
- public override void Close()
- {
- if (isClosed == false)
- {
- isClosed = true;
- Finish();
- if (isStreamOwner)
- {
- baseOutputStream.Close();
- }
- }
- }
- #endregion
-
- #region WriteByte (Public)
- /// <summary>
- /// Writes a single byte to the compressed output stream.
- /// </summary>
- /// <param name="value">The byte value.</param>
- public override void WriteByte(byte value)
- {
- byte[] b = new byte[1];
- b[0] = value;
- Write(b, 0, 1);
- }
- #endregion
-
- #region Write (Public)
- /// <summary>
- /// Writes bytes from an array to the compressed stream.
- /// </summary>
- /// <param name="buffer">The byte array</param>
- /// <param name="offset">The offset into the byte array where to start.
- /// </param>
- /// <param name="count">The number of bytes to write.</param>
- public override void Write(byte[] buffer, int offset, int count)
- {
- deflater.SetInput(buffer, offset, count);
- Deflate();
- }
- #endregion
-
- #region Methods (Private)
-
- #region Deflate
- /// <summary>
- /// Deflates everything in the input buffers. This will call
- /// <code>def.deflate()</code> until all bytes from the input buffers
- /// are processed.
- /// </summary>
- protected void Deflate()
- {
- while (deflater.IsNeedingInput == false)
- {
- int len = deflater.Deflate(buffer, 0, buffer.Length);
-
- if (len <= 0)
- {
- break;
- }
-
- if (keys != null)
- {
- EncryptBlock(buffer, 0, len);
- }
-
- baseOutputStream.Write(buffer, 0, len);
- }
-
- if (deflater.IsNeedingInput == false)
- {
- throw new CompressionException(
- "DeflaterOutputStream can't deflate all input?");
- }
- }
- #endregion
-
- #region EncryptByte
- /// <summary>
- /// Encrypt a single byte
- /// </summary>
- /// <returns>
- /// The encrypted value
- /// </returns>
- protected byte EncryptByte()
- {
- uint temp = ((keys[2] & 0xFFFF) | 2);
- return (byte)((temp * (temp ^ 1)) >> 8);
- }
- #endregion
-
- #region EncryptBlock
- /// <summary>
- /// Encrypt a block of data
- /// </summary>
- /// <param name="buffer">
- /// Data to encrypt. NOTE the original contents of the buffer are lost
- /// </param>
- /// <param name="offset">
- /// Offset of first byte in buffer to encrypt
- /// </param>
- /// <param name="length">
- /// Number of bytes in buffer to encrypt
- /// </param>
- protected void EncryptBlock(byte[] buffer, int offset, int length)
- {
- // Note: refactor to use crypto transform
- for (int i = offset; i < offset + length; ++i)
- {
- byte oldbyte = buffer[i];
- buffer[i] ^= EncryptByte();
- UpdateKeys(oldbyte);
- }
- }
- #endregion
-
- #region InitializePassword
- /// <summary>
- /// Initializes encryption keys based on given password
- /// </summary>
- protected void InitializePassword(string setPassword)
- {
- keys = new uint[]
- {
- 0x12345678,
- 0x23456789,
- 0x34567890
- };
-
- for (int i = 0; i < setPassword.Length; ++i)
- {
- UpdateKeys((byte)setPassword[i]);
- }
- }
- #endregion
-
- #region UpdateKeys
- /// <summary>
- /// Update encryption keys
- /// </summary>
- 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
-
- #endregion
- }
- }