using System; namespace Compress.Utils { class CRCStream { } /// /// A Stream that calculates a CRC32 (a checksum) on all bytes read, /// or on all bytes written. /// /// /// /// /// This class can be used to verify the CRC of a ZipEntry when /// reading from a stream, or to calculate a CRC when writing to a /// stream. The stream should be used to either read, or write, but /// not both. If you intermix reads and writes, the results are not /// defined. /// /// /// /// This class is intended primarily for use internally by the /// DotNetZip library. /// /// public class CrcCalculatorStream : System.IO.Stream, System.IDisposable { private static readonly Int64 UnsetLengthLimit = -99; internal System.IO.Stream _innerStream; private CRC _Crc32; private Int64 _lengthLimit = -99; private bool _leaveOpen; /// /// The default constructor. /// /// /// /// Instances returned from this constructor will leave the underlying /// stream open upon Close(). The stream uses the default CRC32 /// algorithm, which implies a polynomial of 0xEDB88320. /// /// /// The underlying stream public CrcCalculatorStream(System.IO.Stream stream) : this(true, UnsetLengthLimit, stream, null) { } /// /// The constructor allows the caller to specify how to handle the /// underlying stream at close. /// /// /// /// The stream uses the default CRC32 algorithm, which implies a /// polynomial of 0xEDB88320. /// /// /// The underlying stream /// true to leave the underlying stream /// open upon close of the CrcCalculatorStream; false otherwise. public CrcCalculatorStream(System.IO.Stream stream, bool leaveOpen) : this(leaveOpen, UnsetLengthLimit, stream, null) { } /// /// A constructor allowing the specification of the length of the stream /// to read. /// /// /// /// The stream uses the default CRC32 algorithm, which implies a /// polynomial of 0xEDB88320. /// /// /// Instances returned from this constructor will leave the underlying /// stream open upon Close(). /// /// /// The underlying stream /// The length of the stream to slurp public CrcCalculatorStream(System.IO.Stream stream, Int64 length) : this(true, length, stream, null) { if (length < 0) throw new ArgumentException("length"); } /// /// A constructor allowing the specification of the length of the stream /// to read, as well as whether to keep the underlying stream open upon /// Close(). /// /// /// /// The stream uses the default CRC32 algorithm, which implies a /// polynomial of 0xEDB88320. /// /// /// The underlying stream /// The length of the stream to slurp /// true to leave the underlying stream /// open upon close of the CrcCalculatorStream; false otherwise. public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen) : this(leaveOpen, length, stream, null) { if (length < 0) throw new ArgumentException("length"); } /// /// A constructor allowing the specification of the length of the stream /// to read, as well as whether to keep the underlying stream open upon /// Close(), and the CRC32 instance to use. /// /// /// /// The stream uses the specified CRC32 instance, which allows the /// application to specify how the CRC gets calculated. /// /// /// The underlying stream /// The length of the stream to slurp /// true to leave the underlying stream /// open upon close of the CrcCalculatorStream; false otherwise. /// the CRC32 instance to use to calculate the CRC32 public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen, CRC crc32) : this(leaveOpen, length, stream, crc32) { if (length < 0) throw new ArgumentException("length"); } // This ctor is private - no validation is done here. This is to allow the use // of a (specific) negative value for the _lengthLimit, to indicate that there // is no length set. So we validate the length limit in those ctors that use an // explicit param, otherwise we don't validate, because it could be our special // value. private CrcCalculatorStream (bool leaveOpen, Int64 length, System.IO.Stream stream, CRC crc32) : base() { _innerStream = stream; _Crc32 = crc32 ?? new CRC(); _lengthLimit = length; _leaveOpen = leaveOpen; } /// /// Gets the total number of bytes run through the CRC32 calculator. /// /// /// /// This is either the total number of bytes read, or the total number of /// bytes written, depending on the direction of this stream. /// public Int64 TotalBytesSlurped { get { return _Crc32.TotalBytesRead; } } /// /// Provides the current CRC for all blocks slurped in. /// /// /// /// The running total of the CRC is kept as data is written or read /// through the stream. read this property after all reads or writes to /// get an accurate CRC for the entire stream. /// /// public Int32 Crc { get { return _Crc32.Crc32Result; } } /// /// Indicates whether the underlying stream will be left open when the /// CrcCalculatorStream is Closed. /// /// /// /// Set this at any point before calling . /// /// public bool LeaveOpen { get { return _leaveOpen; } set { _leaveOpen = value; } } /// /// Read from the stream /// /// the buffer to read /// the offset at which to start /// the number of bytes to read /// the number of bytes actually read public override int Read(byte[] buffer, int offset, int count) { int bytesToRead = count; // Need to limit the # of bytes returned, if the stream is intended to have // a definite length. This is especially useful when returning a stream for // the uncompressed data directly to the application. The app won't // necessarily read only the UncompressedSize number of bytes. For example // wrapping the stream returned from OpenReader() into a StreadReader() and // calling ReadToEnd() on it, We can "over-read" the zip data and get a // corrupt string. The length limits that, prevents that problem. if (_lengthLimit != UnsetLengthLimit) { if (_Crc32.TotalBytesRead >= _lengthLimit) return 0; // EOF Int64 bytesRemaining = _lengthLimit - _Crc32.TotalBytesRead; if (bytesRemaining < count) bytesToRead = (int)bytesRemaining; } int n = _innerStream.Read(buffer, offset, bytesToRead); if (n > 0) _Crc32.SlurpBlock(buffer, offset, n); return n; } /// /// Write to the stream. /// /// the buffer from which to write /// the offset at which to start writing /// the number of bytes to write public override void Write(byte[] buffer, int offset, int count) { if (count > 0) _Crc32.SlurpBlock(buffer, offset, count); _innerStream.Write(buffer, offset, count); } /// /// Indicates whether the stream supports reading. /// public override bool CanRead { get { return _innerStream.CanRead; } } /// /// Indicates whether the stream supports seeking. /// /// /// /// Always returns false. /// /// public override bool CanSeek { get { return false; } } /// /// Indicates whether the stream supports writing. /// public override bool CanWrite { get { return _innerStream.CanWrite; } } /// /// Flush the stream. /// public override void Flush() { _innerStream.Flush(); } /// /// Returns the length of the underlying stream. /// public override long Length { get { if (_lengthLimit == CrcCalculatorStream.UnsetLengthLimit) return _innerStream.Length; else return _lengthLimit; } } /// /// The getter for this property returns the total bytes read. /// If you use the setter, it will throw /// . /// public override long Position { get { return _Crc32.TotalBytesRead; } set { throw new NotSupportedException(); } } /// /// Seeking is not supported on this stream. This method always throws /// /// /// N/A /// N/A /// N/A public override long Seek(long offset, System.IO.SeekOrigin origin) { throw new NotSupportedException(); } /// /// This method always throws /// /// /// N/A public override void SetLength(long value) { throw new NotSupportedException(); } void IDisposable.Dispose() { Close(); } /// /// Closes the stream. /// public override void Close() { base.Close(); if (!_leaveOpen) _innerStream.Close(); } } }