// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.Dtf.Compression.Zip { using System; using System.IO; using System.Diagnostics.CodeAnalysis; /// /// Wraps a source stream and calcaluates a CRC over all bytes that are read or written. /// /// /// The CRC algorithm matches that used in the standard ZIP file format. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")] public class CrcStream : Stream { private Stream source; private uint crc; /// /// Creates a new CrcStream instance from a source stream. /// /// Underlying stream where bytes will be read from or written to. public CrcStream(Stream source) { this.source = source; } /// /// Gets the current CRC over all bytes that have been read or written /// since this instance was created. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")] public uint Crc { get { return this.crc; } } /// /// Gets the underlying stream that this stream reads from or writes to. /// public Stream Source { get { return this.source; } } /// /// Gets a value indicating whether the source stream supports reading. /// /// true if the stream supports reading; otherwise, false. public override bool CanRead { get { return this.source.CanRead; } } /// /// Gets a value indicating whether the source stream supports writing. /// /// true if the stream supports writing; otherwise, false. public override bool CanWrite { get { return this.source.CanWrite; } } /// /// Gets a value indicating whether the source stream supports seeking. /// /// true if the stream supports seeking; otherwise, false. public override bool CanSeek { get { return this.source.CanSeek; } } /// /// Gets the length of the source stream. /// public override long Length { get { return this.source.Length; } } /// /// Gets or sets the position of the source stream. /// public override long Position { get { return this.source.Position; } set { this.source.Position = value; } } /// /// Sets the position within the source stream. /// /// A byte offset relative to the origin parameter. /// A value of type SeekOrigin indicating /// the reference point used to obtain the new position. /// The new position within the source stream. /// /// Note the CRC is only calculated over bytes that are actually read or /// written, so any bytes skipped by seeking will not contribute to the CRC. /// public override long Seek(long offset, SeekOrigin origin) { return this.source.Seek(offset, origin); } /// /// Sets the length of the source stream. /// /// The desired length of the /// stream in bytes. public override void SetLength(long value) { this.source.SetLength(value); } /// /// Reads a sequence of bytes from the source stream and advances /// the position within the stream by the number of bytes read. /// /// An array of bytes. When this method returns, the buffer /// contains the specified byte array with the values between offset and /// (offset + count - 1) replaced by the bytes read from the current source. /// The zero-based byte offset in buffer at which to begin /// storing the data read from the current stream. /// The maximum number of bytes to be read from the current stream. /// The total number of bytes read into the buffer. This can be less /// than the number of bytes requested if that many bytes are not currently available, /// or zero (0) if the end of the stream has been reached. public override int Read(byte[] buffer, int offset, int count) { count = this.source.Read(buffer, offset, count); this.UpdateCrc(buffer, offset, count); return count; } /// /// Writes a sequence of bytes to the source stream and advances the /// current position within this stream by the number of bytes written. /// /// An array of bytes. This method copies count /// bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which /// to begin copying bytes to the current stream. /// The number of bytes to be written to the /// current stream. public override void Write(byte[] buffer, int offset, int count) { this.source.Write(buffer, offset, count); this.UpdateCrc(buffer, offset, count); } /// /// Flushes the source stream. /// public override void Flush() { this.source.Flush(); } /// /// Closes the underlying stream. /// public override void Close() { this.source.Close(); base.Close(); } /// /// Updates the CRC with a range of bytes that were read or written. /// private void UpdateCrc(byte[] buffer, int offset, int count) { this.crc = ~this.crc; for( ; count > 0; count--, offset++) { this.crc = (this.crc >> 8) ^ CrcStream.crcTable[(this.crc & 0xFF) ^ buffer[offset]]; } this.crc = ~this.crc; } private static uint[] crcTable = MakeCrcTable(); /// /// Computes a table that speeds up calculation of the CRC. /// private static uint[] MakeCrcTable() { const uint poly = 0x04C11DB7u; uint[] crcTable = new uint[256]; for(uint n = 0; n < 256; n++) { uint c = CrcStream.Reflect(n, 8); c = c << 24; for(uint k = 0; k < 8; k++) { c = (c << 1) ^ ((c & 0x80000000u) != 0 ? poly : 0); } crcTable[n] = CrcStream.Reflect(c, 32); } return crcTable; } /// /// Reflects the ordering of certain number of bits. For exmample when reflecting /// one byte, bit one is swapped with bit eight, bit two with bit seven, etc. /// private static uint Reflect(uint value, int bits) { for (int i = 0; i < bits / 2; i++) { uint leftBit = 1u << (bits - 1 - i); uint rightBit = 1u << i; if (((value & leftBit) != 0) != ((value & rightBit) != 0)) { value ^= leftBit | rightBit; } } return value; } } }