Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/.vs/ChecksumValidator/project-colors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Version": 1,
"ProjectMap": {
"67aa0267-46db-4857-bfc1-87c9ab535542": {
"ProjectGuid": "67aa0267-46db-4857-bfc1-87c9ab535542",
"DisplayName": "Gardian.Utilities.ChecksumValidator",
"ColorIndex": 0
}
},
"NextColorIndex": 1
}
20 changes: 20 additions & 0 deletions src/Backup/ChecksumValidator.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gardian.Utilities.ChecksumValidator", "Gardian.Utilities.ChecksumValidator\Gardian.Utilities.ChecksumValidator.csproj", "{67AA0267-46DB-4857-BFC1-87C9AB535542}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{67AA0267-46DB-4857-BFC1-87C9AB535542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67AA0267-46DB-4857-BFC1-87C9AB535542}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67AA0267-46DB-4857-BFC1-87C9AB535542}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67AA0267-46DB-4857-BFC1-87C9AB535542}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
145 changes: 145 additions & 0 deletions src/Backup/Gardian.Utilities.ChecksumValidator/CRC32.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Author: Damien Guard, (c) 2006
// (http://damieng.com/blog/2006/08/08/Calculating_CRC32_in_C_and_NET)

using System;
using System.Security.Cryptography;

/// <summary>
/// </summary>
public sealed class CRC32 : HashAlgorithm
{

/// <summary>
/// Default polynomial used for CRC32 computation.
/// </summary>
[CLSCompliant(false)]
public const UInt32 DefaultPolynomial = 0xedb88320;

/// <summary>
/// Default CRC32 computation seed.
/// </summary>
[CLSCompliant(false)]
public const UInt32 DefaultSeed = 0xffffffff;

private UInt32 _hash;
private readonly UInt32 _seed;
private readonly UInt32[] _table;
private static UInt32[] DefaultTable;

///<summary>
///</summary>
public CRC32()
{
this._table = InitializeTable(DefaultPolynomial);
this._seed = DefaultSeed;
Initialize();
}

///<summary>
///</summary>
[CLSCompliant(false)]
public CRC32(UInt32 polynomial, UInt32 seed)
{
this._table = InitializeTable(polynomial);
this._seed = seed;
Initialize();
}

/// <summary>
/// </summary>
public override void Initialize()
{
this._hash = this._seed;
}

/// <summary>
/// </summary>
protected override void HashCore(byte[] buffer, int start, int length)
{
this._hash = CalculateHash(this._table, this._hash, buffer, start, length);
}

/// <summary>
/// </summary>
protected override byte[] HashFinal()
{
byte[] hashBuffer = UInt32ToBigEndianBytes(~this._hash);
this.HashValue = hashBuffer;
return hashBuffer;
}

/// <summary>
/// </summary>
public override int HashSize
{
get { return 32; }
}

/// <summary>
/// </summary>
[CLSCompliant(false)]
public static UInt32 Compute(byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
}

/// <summary>
/// </summary>
[CLSCompliant(false)]
public static UInt32 Compute(UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
}

/// <summary>
/// </summary>
[CLSCompliant(false)]
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
}

private static UInt32[] InitializeTable(UInt32 polynomial)
{
if (polynomial == DefaultPolynomial && DefaultTable != null)
return DefaultTable;

var createTable = new UInt32[256];
for (int i = 0; i < 256; i++)
{
var entry = (UInt32)i;
for (int j = 0; j < 8; j++)
if ((entry & 1) == 1)
entry = (entry >> 1) ^ polynomial;
else
entry = entry >> 1;
createTable[i] = entry;
}

if (polynomial == DefaultPolynomial)
DefaultTable = createTable;

return createTable;
}

private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
{
UInt32 crc = seed;
for (int i = start; i < size; i++)
unchecked
{
crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
}
return crc;
}

private static byte[] UInt32ToBigEndianBytes(UInt32 x)
{
return new[] {
(byte)((x >> 24) & 0xff),
(byte)((x >> 16) & 0xff),
(byte)((x >> 8) & 0xff),
(byte)(x & 0xff)
};
}
}
184 changes: 184 additions & 0 deletions src/Backup/Gardian.Utilities.ChecksumValidator/Checksum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace Gardian.Utilities.ChecksumValidator
{

/// <summary>
/// Checksum computation implementation.
/// </summary>
internal static class Checksum
{

//**************************************************
//* Public interface
//**************************************************

//-------------------------------------------------
/// <summary>
/// Compute checksum for the specified source file,
/// return it as hex string ("F3BA87...").
/// </summary>
/// <remarks>
/// This method will be run on a worker thread
/// (from thread pool) via async invocation.
/// </remarks>
public static string ComputeChecksum(string sourceFile, ChecksumMethod method, Action<decimal> progressNotifier)
{
System.Threading.Thread.Sleep(100);
using (var hashAlgorithm = CreateHashAlgorithm(method))
using (var source = new TrackingStream(File.OpenRead(sourceFile), progressNotifier))
{
var hash = hashAlgorithm.ComputeHash(source);

var msg = new StringBuilder(128);
foreach (var byteValue in hash)
{
msg.AppendFormat(byteValue.ToString("X2", CultureInfo.InvariantCulture));
}
return msg.ToString();
}
}


//-------------------------------------------------
/// <summary>
/// Flag that requests cancellation of the current computation
/// (if set to true and a computation is ongoing).
/// </summary>
/// <remarks>
/// Field marked as volatile because it will be accessed
/// by multiple threads (UI thread to set it, worker
/// thread to check it).
/// </remarks>
public static volatile bool Cancel;




//**************************************************
//* Private
//**************************************************

//-------------------------------------------------
/// <summary>
/// Find hash algorithm that corresponds to the
/// requested method
/// </summary>
/// <exception cref="NotSupportedException">Thrown
/// when requested method is not recognized.</exception>
private static HashAlgorithm CreateHashAlgorithm(ChecksumMethod method)
{
switch (method)
{
case ChecksumMethod.SHA1: return new SHA1CryptoServiceProvider();
case ChecksumMethod.MD5: return new MD5CryptoServiceProvider();
case ChecksumMethod.CRC32: return new CRC32();
default: throw new NotSupportedException(string.Concat("Requested checksum method ", method, " is not supported"));
}
}


//-------------------------------------------------
/// <summary>
/// Custom stream for hash algorithm that only implements
/// Read &amp; Dispose methods (every other method will
/// throw NotSupportedException). This class implements
/// a bridge pattern (both Read and Dispose are forwarded
/// to the underlying "tracked" stream). The only extra
/// functionality is that if Read operation read another
/// megabyte of data since last progress notifier invocation,
/// progress percentage is recomputed and progress notifier
/// is called with this value. This allows us to track
/// progress of hash computation even though HashAlgorithm
/// does not support this.
/// </summary>
private sealed class TrackingStream : Stream
{
public TrackingStream(Stream trackedStream, Action<decimal> progressNotifier)
{
if (trackedStream == null) { throw new ArgumentNullException("trackedStream"); }
this._trackedStream = trackedStream;
this._progressNotifier = progressNotifier;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this._trackedStream.Dispose();
}
}
public override void Flush()
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
var ret = this._trackedStream.Read(buffer, offset, count);

if (Checksum.Cancel)
{
throw new ThreadInterruptedException();
}
if (this._progressNotifier != null)
{
var position = this._trackedStream.Position;
const long mb = 2L << 19;
var megabyte = position / mb;
if (megabyte > this._lastMegabyte)
{
this._lastMegabyte = megabyte;
var percentage = ((decimal)position / this._trackedStream.Length);
this._progressNotifier(percentage);
}
}
return ret;
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}

private long _lastMegabyte;
private readonly Action<decimal> _progressNotifier;
private readonly Stream _trackedStream;

}

}

}
17 changes: 17 additions & 0 deletions src/Backup/Gardian.Utilities.ChecksumValidator/ChecksumMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Gardian.Utilities.ChecksumValidator
{

/// <summary>
/// Define supported checksum (hash) methods.
/// </summary>
internal enum ChecksumMethod
{
// ReSharper disable InconsistentNaming
None = 0,
SHA1,
MD5,
CRC32,
// ReSharper restore InconsistentNaming
}

}
Loading