// 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;
}
}
}