// DeflateStream.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009-2010 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2010-February-05 08:49:04>
//
// ------------------------------------------------------------------
//
// This module defines the DeflateStream class, which can be used as a replacement for
// the System.IO.Compression.DeflateStream class in the .NET BCL.
//
// ------------------------------------------------------------------
using System;
using System.IO;
using System.Text;
namespace SharpCompress.Compressors.Deflate
{
public class DeflateStream : Stream
{
private readonly ZlibBaseStream _baseStream;
private bool _disposed;
public DeflateStream(Stream stream, CompressionMode mode,
CompressionLevel level = CompressionLevel.Default,
Encoding forceEncoding = null)
{
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, forceEncoding);
}
#region Zlib properties
///
/// This property sets the flush behavior on the stream.
///
/// See the ZLIB documentation for the meaning of the flush behavior.
///
public virtual FlushType FlushMode
{
get => (_baseStream._flushMode);
set
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream._flushMode = value;
}
}
///
/// The size of the working buffer for the compression codec.
///
///
///
///
/// The working buffer is used for all stream operations. The default size is
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
/// with a larger buffer. Then again, you might not. You would have to test
/// it.
///
///
///
/// Set this before the first call to Read() or Write() on the
/// stream. If you try to set it afterwards, it will throw.
///
///
public int BufferSize
{
get => _baseStream._bufferSize;
set
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
if (_baseStream._workingBuffer != null)
{
throw new ZlibException("The working buffer is already set.");
}
if (value < ZlibConstants.WorkingBufferSizeMin)
{
throw new ZlibException(
String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value,
ZlibConstants.WorkingBufferSizeMin));
}
_baseStream._bufferSize = value;
}
}
///
/// The ZLIB strategy to be used during compression.
///
///
///
/// By tweaking this parameter, you may be able to optimize the compression for
/// data with particular characteristics.
///
public CompressionStrategy Strategy
{
get => _baseStream.Strategy;
set
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.Strategy = value;
}
}
/// Returns the total number of bytes input so far.
public virtual long TotalIn => _baseStream._z.TotalBytesIn;
/// Returns the total number of bytes output so far.
public virtual long TotalOut => _baseStream._z.TotalBytesOut;
#endregion
#region System.IO.Stream methods
///
/// Indicates whether the stream can be read.
///
///
/// The return value depends on whether the captive stream supports reading.
///
public override bool CanRead
{
get
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream._stream.CanRead;
}
}
///
/// Indicates whether the stream supports Seek operations.
///
///
/// Always returns false.
///
public override bool CanSeek => false;
///
/// Indicates whether the stream can be written.
///
///
/// The return value depends on whether the captive stream supports writing.
///
public override bool CanWrite
{
get
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream._stream.CanWrite;
}
}
///
/// Reading this property always throws a .
///
public override long Length => throw new NotSupportedException();
///
/// The position of the stream pointer.
///
///
///
/// Setting this property always throws a . Reading will return the total bytes
/// written out, if used in writing, or the total bytes read in, if used in
/// reading. The count may refer to compressed bytes or uncompressed bytes,
/// depending on how you've used the stream.
///
public override long Position
{
get
{
if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer)
{
return _baseStream._z.TotalBytesOut;
}
if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader)
{
return _baseStream._z.TotalBytesIn;
}
return 0;
}
set => throw new NotSupportedException();
}
///
/// Dispose the stream.
///
///
/// This may or may not result in a Close() call on the captive stream.
///
protected override void Dispose(bool disposing)
{
try
{
if (!_disposed)
{
if (disposing)
{
_baseStream?.Dispose();
}
_disposed = true;
}
}
finally
{
base.Dispose(disposing);
}
}
///
/// Flush the stream.
///
public override void Flush()
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.Flush();
}
///
/// Read data from the stream.
///
///
///
///
/// If you wish to use the DeflateStream to compress data while
/// reading, you can create a DeflateStream with
/// CompressionMode.Compress, providing an uncompressed data stream.
/// Then call Read() on that DeflateStream, and the data read will be
/// compressed as you read. If you wish to use the DeflateStream to
/// decompress data while reading, you can create a DeflateStream with
/// CompressionMode.Decompress, providing a readable compressed data
/// stream. Then call Read() on that DeflateStream, and the data read
/// will be decompressed as you read.
///
///
///
/// A DeflateStream can be used for Read() or Write(), but not both.
///
///
///
/// The buffer into which the read data should be placed.
/// the offset within that data array to put the first byte read.
/// the number of bytes to read.
/// the number of bytes actually read
public override int Read(byte[] buffer, int offset, int count)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream.Read(buffer, offset, count);
}
public override int ReadByte()
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream.ReadByte();
}
///
/// Calling this method always throws a .
///
/// this is irrelevant, since it will always throw!
/// this is irrelevant, since it will always throw!
/// irrelevant!
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
///
/// Calling this method always throws a .
///
/// this is irrelevant, since it will always throw!
public override void SetLength(long value)
{
throw new NotSupportedException();
}
///
/// Write data to the stream.
///
///
///
///
/// If you wish to use the DeflateStream to compress data while
/// writing, you can create a DeflateStream with
/// CompressionMode.Compress, and a writable output stream. Then call
/// Write() on that DeflateStream, providing uncompressed data
/// as input. The data sent to the output stream will be the compressed form
/// of the data written. If you wish to use the DeflateStream to
/// decompress data while writing, you can create a DeflateStream with
/// CompressionMode.Decompress, and a writable output stream. Then
/// call Write() on that stream, providing previously compressed
/// data. The data sent to the output stream will be the decompressed form of
/// the data written.
///
///
///
/// A DeflateStream can be used for Read() or Write(),
/// but not both.
///
///
///
///
/// The buffer holding data to write to the stream.
/// the offset within that data array to find the first byte to write.
/// the number of bytes to write.
public override void Write(byte[] buffer, int offset, int count)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.WriteByte(value);
}
#endregion
public MemoryStream InputBuffer => new MemoryStream(_baseStream._z.InputBuffer, _baseStream._z.NextIn,
_baseStream._z.AvailableBytesIn);
}
}