Skip to main content
added 3 characters in body
Source Link
Jesse C. Slicer
  • 14.1k
  • 1
  • 39
  • 51

Here's how I would go about it (mostly from a readability standpoint rather than a performance one - though the use of immutable data may help contribute to well-measurablmeasurable performance and memory usage metrics):

namespace MD5Library
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;

    public interface IFileBlock
    {
        byte[] Data
        {
            get;
        }

        int Length
        {
            get;

            set;
        }
    }

    public sealed class FileBlock : IFileBlock
    {
        private readonly byte[] data;

        public FileBlock(int inBufferSize)
        {
            this.data = new byte[inBufferSize];
        }

        public byte[] Data
        {
            get
            {
                return this.data;
            }
        }

        public int Length
        {
            get;

            set;
        }
    }

    public class MD5Hasher
    {
        private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

        public MD5Hasher()
        {
        }

        public MD5Hasher(int bufferSize)
        {
            this.bufferSize = bufferSize;
        }

        protected int BufferSize
        {
            get
            {
                return this.bufferSize;
            }
        }

        public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            // Queue with capacity 3x the buffer.
            var fi = new FileInfo(inFile);
            var bigBuffer = new BlockingCollection<IFileBlock>(3);
            var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
            var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

            readFileTask.Start();
            computeHashTask.Start();
            return computeHashTask;
        }

        private static string HashToString(IEnumerable<byte> inHash)
        {
            return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
        }

        private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
        {
            using (var stream = File.OpenRead(fi.FullName))
            {
                int bytesRead;

                do
                {
                    var fileBlock = new FileBlock(this.bufferSize);

                    fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                    bytesRead = fileBlock.Length;

                    // Infinite timeout.
                    bigBuffer.TryAdd(fileBlock, -1, ct);
                }
                while (bytesRead != 0);

                bigBuffer.CompleteAdding();
            }
        }

        private static string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            var fileSize = fi.Length;
            var bytesProcessed = 0L;

            using (HashAlgorithm hashAlgorithm = MD5.Create())
            {
                foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                {
                    hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                    bytesProcessed += b.Length;

                    // Report progress.
                    prg.Report(new ProgressReport(bytesProcessed, fileSize));
                }

                hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                return HashToString(hashAlgorithm.Hash);
            }
        }
    }

    public interface IProgressReport
    {
        int PercentDone
        {
            get;
        }

        string InfoText
        {
            get;
        }
    }

    public sealed class ProgressReport : IProgressReport
    {
        private const string PercentComplete = "% complete.";

        private readonly int percentDone;

        private readonly string infoText;

        public ProgressReport(long progress, long total)
        {
            this.percentDone = Convert.ToInt32((100 * progress) / total);
            this.infoText = this.PercentDone + PercentComplete;
        }

        public int PercentDone
        {
            get
            {
                return this.percentDone;
            }
        }

        public string InfoText
        {
            get
            {
                return this.infoText;
            }
        }
    }

    // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
    public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
    {
        private readonly SynchronizationContext context;

        private T progress;

        public PropertyProgress(T initialProgress = default(T))
        {
            this.context = SynchronizationContext.Current ?? new SynchronizationContext();
            this.progress = initialProgress;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public T Progress
        {
            get
            {
                return this.progress;
            }

            private set
            {
                this.progress = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                }
            }
        }

        void IProgress<T>.Report(T value)
        {
            this.context.Post(_ => { this.Progress = value; }, null);
        }
    }
}

Here's how I would go about it (mostly from a readability standpoint rather than a performance one - though the use of immutable data may help contribute to well-measurabl performance and memory usage metrics):

namespace MD5Library
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;

    public interface IFileBlock
    {
        byte[] Data
        {
            get;
        }

        int Length
        {
            get;

            set;
        }
    }

    public sealed class FileBlock : IFileBlock
    {
        private readonly byte[] data;

        public FileBlock(int inBufferSize)
        {
            this.data = new byte[inBufferSize];
        }

        public byte[] Data
        {
            get
            {
                return this.data;
            }
        }

        public int Length
        {
            get;

            set;
        }
    }

    public class MD5Hasher
    {
        private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

        public MD5Hasher()
        {
        }

        public MD5Hasher(int bufferSize)
        {
            this.bufferSize = bufferSize;
        }

        protected int BufferSize
        {
            get
            {
                return this.bufferSize;
            }
        }

        public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            // Queue with capacity 3x the buffer.
            var fi = new FileInfo(inFile);
            var bigBuffer = new BlockingCollection<IFileBlock>(3);
            var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
            var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

            readFileTask.Start();
            computeHashTask.Start();
            return computeHashTask;
        }

        private static string HashToString(IEnumerable<byte> inHash)
        {
            return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
        }

        private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
        {
            using (var stream = File.OpenRead(fi.FullName))
            {
                int bytesRead;

                do
                {
                    var fileBlock = new FileBlock(this.bufferSize);

                    fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                    bytesRead = fileBlock.Length;

                    // Infinite timeout.
                    bigBuffer.TryAdd(fileBlock, -1, ct);
                }
                while (bytesRead != 0);

                bigBuffer.CompleteAdding();
            }
        }

        private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            var fileSize = fi.Length;
            var bytesProcessed = 0L;

            using (HashAlgorithm hashAlgorithm = MD5.Create())
            {
                foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                {
                    hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                    bytesProcessed += b.Length;

                    // Report progress.
                    prg.Report(new ProgressReport(bytesProcessed, fileSize));
                }

                hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                return HashToString(hashAlgorithm.Hash);
            }
        }
    }

    public interface IProgressReport
    {
        int PercentDone
        {
            get;
        }

        string InfoText
        {
            get;
        }
    }

    public sealed class ProgressReport : IProgressReport
    {
        private const string PercentComplete = "% complete.";

        private readonly int percentDone;

        private readonly string infoText;

        public ProgressReport(long progress, long total)
        {
            this.percentDone = Convert.ToInt32((100 * progress) / total);
            this.infoText = this.PercentDone + PercentComplete;
        }

        public int PercentDone
        {
            get
            {
                return this.percentDone;
            }
        }

        public string InfoText
        {
            get
            {
                return this.infoText;
            }
        }
    }

    // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
    public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
    {
        private readonly SynchronizationContext context;

        private T progress;

        public PropertyProgress(T initialProgress = default(T))
        {
            this.context = SynchronizationContext.Current ?? new SynchronizationContext();
            this.progress = initialProgress;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public T Progress
        {
            get
            {
                return this.progress;
            }

            private set
            {
                this.progress = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                }
            }
        }

        void IProgress<T>.Report(T value)
        {
            this.context.Post(_ => { this.Progress = value; }, null);
        }
    }
}

Here's how I would go about it (mostly from a readability standpoint rather than a performance one - though the use of immutable data may help contribute to well-measurable performance and memory usage metrics):

namespace MD5Library
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;

    public interface IFileBlock
    {
        byte[] Data
        {
            get;
        }

        int Length
        {
            get;

            set;
        }
    }

    public sealed class FileBlock : IFileBlock
    {
        private readonly byte[] data;

        public FileBlock(int inBufferSize)
        {
            this.data = new byte[inBufferSize];
        }

        public byte[] Data
        {
            get
            {
                return this.data;
            }
        }

        public int Length
        {
            get;

            set;
        }
    }

    public class MD5Hasher
    {
        private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

        public MD5Hasher()
        {
        }

        public MD5Hasher(int bufferSize)
        {
            this.bufferSize = bufferSize;
        }

        protected int BufferSize
        {
            get
            {
                return this.bufferSize;
            }
        }

        public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            // Queue with capacity 3x the buffer.
            var fi = new FileInfo(inFile);
            var bigBuffer = new BlockingCollection<IFileBlock>(3);
            var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
            var computeHashTask = new Task<string>(() => CalcHash(fi, bigBuffer, ct, prg));

            readFileTask.Start();
            computeHashTask.Start();
            return computeHashTask;
        }

        private static string HashToString(IEnumerable<byte> inHash)
        {
            return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
        }

        private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
        {
            using (var stream = File.OpenRead(fi.FullName))
            {
                int bytesRead;

                do
                {
                    var fileBlock = new FileBlock(this.bufferSize);

                    fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                    bytesRead = fileBlock.Length;

                    // Infinite timeout.
                    bigBuffer.TryAdd(fileBlock, -1, ct);
                }
                while (bytesRead != 0);

                bigBuffer.CompleteAdding();
            }
        }

        private static string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            var fileSize = fi.Length;
            var bytesProcessed = 0L;

            using (HashAlgorithm hashAlgorithm = MD5.Create())
            {
                foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                {
                    hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                    bytesProcessed += b.Length;

                    // Report progress.
                    prg.Report(new ProgressReport(bytesProcessed, fileSize));
                }

                hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                return HashToString(hashAlgorithm.Hash);
            }
        }
    }

    public interface IProgressReport
    {
        int PercentDone
        {
            get;
        }

        string InfoText
        {
            get;
        }
    }

    public sealed class ProgressReport : IProgressReport
    {
        private const string PercentComplete = "% complete.";

        private readonly int percentDone;

        private readonly string infoText;

        public ProgressReport(long progress, long total)
        {
            this.percentDone = Convert.ToInt32((100 * progress) / total);
            this.infoText = this.PercentDone + PercentComplete;
        }

        public int PercentDone
        {
            get
            {
                return this.percentDone;
            }
        }

        public string InfoText
        {
            get
            {
                return this.infoText;
            }
        }
    }

    // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
    public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
    {
        private readonly SynchronizationContext context;

        private T progress;

        public PropertyProgress(T initialProgress = default(T))
        {
            this.context = SynchronizationContext.Current ?? new SynchronizationContext();
            this.progress = initialProgress;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public T Progress
        {
            get
            {
                return this.progress;
            }

            private set
            {
                this.progress = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                }
            }
        }

        void IProgress<T>.Report(T value)
        {
            this.context.Post(_ => { this.Progress = value; }, null);
        }
    }
}
code formatting
Source Link
svick
  • 24.3k
  • 4
  • 52
  • 89

namespace MD5Library { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks;

public interfacenamespace IFileBlockMD5Library
{
    byte[]using DataSystem;
    {using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.ComponentModel;
   get; using System.IO;
    }using System.Linq;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;

    intpublic Lengthinterface IFileBlock
    {
        get;
byte[] Data
        set;{
    }        get;
        }

public sealed class FileBlock : IFileBlock   int Length
        {
    private readonly byte[] data;     get;

    public FileBlock(int inBufferSize)
    {
   set;
     this.data = new byte[inBufferSize];}
    }

    public byte[]sealed Dataclass FileBlock : IFileBlock
    {
        getprivate readonly byte[] data;

        public FileBlock(int inBufferSize)
        {
            return this.data;data = new byte[inBufferSize];
        }
    }

        public intbyte[] LengthData
        {
        get;    get
            {
        set;        return this.data;
            }
        }

        public classint MD5Hasher
{Length
    private readonly int bufferSize ={
 1024 * 1024 * 16; // 16mb at a time.  get;

    public MD5Hasher()       set;
    {    }
    }

    public MD5Hasher(intclass bufferSize)MD5Hasher
    {
        this.private readonly int bufferSize = bufferSize;
1024 * 1024 * 16; // }16mb at a time.

    protected int BufferSize
    {
       public getMD5Hasher()
        {
            return this.bufferSize;
        }
    }

    public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
    {
        // Queue with capacity 3x the buffer.
        var fi = newpublic FileInfoMD5Hasher(inFileint bufferSize);
        var bigBuffer = new BlockingCollection<IFileBlock>(3);{
        var readFileTask = new Task(() => this.ReadFiles(fi,bufferSize ct,= bigBuffer));bufferSize;
        var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));}

        readFileTask.Start();protected int BufferSize
        computeHashTask.Start();{
            get
            {
                return computeHashTask;this.bufferSize;
            }
        }

    private static string HashToString public Task<string> Go(IEnumerable<byte>string inHashinFile, CancellationToken ct, IProgress<IProgressReport> prg)
        {
        return inHash.Aggregate(string   // Queue with capacity 3x the buffer.Empty,
            var fi = new FileInfo(current,inFile);
 b           var bigBuffer = new BlockingCollection<IFileBlock>(3);
 => current + b        var readFileTask = new Task(() => this.ToStringReadFiles("x2"fi, ct, bigBuffer));
    }        var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

    private async void ReadFiles     readFileTask.Start(FileInfo);
 fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer       computeHashTask.Start();
    {        return computeHashTask;
        using}

 (var stream = File.OpenRead    private static string HashToString(fi.FullName)IEnumerable<byte> inHash)
        {
            intreturn bytesRead;inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
        }

        private async void ReadFiles(FileInfo dofi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
        {
            using (var stream = File.OpenRead(fi.FullName))
            {
                var fileBlock = newint FileBlock(this.bufferSize);bytesRead;

                fileBlock.Lengthdo
 = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);           {
                bytesRead =   var fileBlock = new FileBlock(this.Length;bufferSize);

                // Infinite timeout.
             fileBlock.Length = await bigBufferstream.TryAddReadAsync(fileBlock.Data, -10, ctthis.bufferSize);
            }
            while (bytesRead != 0);fileBlock.Length;

                    // Infinite timeout.
                    bigBuffer.CompleteAddingTryAdd(fileBlock, -1, ct);
                }
    }            while (bytesRead != 0);

    private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
    {bigBuffer.CompleteAdding();
        var fileSize = fi.Length;
   }
     var bytesProcessed = 0L;}

        usingprivate string CalcHash(HashAlgorithmFileInfo hashAlgorithmfi, =BlockingCollection<IFileBlock> MD5.Create()bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            foreachvar (fileSize = fi.Length;
            var bbytesProcessed in= bigBuffer0L;

            using (HashAlgorithm hashAlgorithm = MD5.GetConsumingEnumerableCreate(ct))
            {
                foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                {
                    hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                    bytesProcessed += b.Length;

                    // Report progress.
                    prg.Report(new ProgressReport(bytesProcessed, fileSize));
                }

                hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                return HashToString(hashAlgorithm.Hash);
            }
        }
    }
}

public interface IProgressReport
{
   public intinterface PercentDoneIProgressReport
    {
        int PercentDone
        {
            get;
        }

        string InfoText
        {
            get;
        }
    }
}

    public sealed class ProgressReport : IProgressReport
    {
        private const string PercentComplete = "% complete.";

        private readonly int percentDone;

        private readonly string infoText;

        public ProgressReport(long progress, long total)
        {
            this.percentDone = Convert.ToInt32((100 * progress) / total);
            this.infoText = this.PercentDone + PercentComplete;
        }

    public int PercentDone
    {
      public int getPercentDone
        {
            get
            {
                return this.percentDone;
            }
        }
    }

    public string InfoText
    {
      public string getInfoText
        {
            get
            {
                return this.infoText;
            }
        }
    }
}

// Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
{
    private readonly SynchronizationContext context;

    private T progress;

    // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
    public PropertyProgress(Tclass initialProgressPropertyProgress<T> =: default(T))IProgress<T>, INotifyPropertyChanged
    {
        this.context = SynchronizationContext.Current ??private newreadonly SynchronizationContext();
        this.progress = initialProgress;
    }context;

    public event PropertyChangedEventHandler PropertyChanged; private T progress;

    public T Progress
    {
    public PropertyProgress(T initialProgress = getdefault(T))
        {
            returnthis.context = SynchronizationContext.Current ?? new SynchronizationContext();
            this.progress;progress = initialProgress;
        }

        privatepublic setevent PropertyChangedEventHandler PropertyChanged;

        public T Progress
        {
            this.progress = value;
            if (this.PropertyChanged != null)get
            {
                return this.progress;
            }

            private set
            {
                this.progress = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                }
            }
        }
    }

        void IProgress<T>.Report(T value)
        {
            this.context.Post(_ => { this.Progress = value; }, null);
        }
    }
}

}

namespace MD5Library { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks;

public interface IFileBlock
{
    byte[] Data
    {
        get;
    }

    int Length
    {
        get;

        set;
    }
}

public sealed class FileBlock : IFileBlock
{
    private readonly byte[] data;

    public FileBlock(int inBufferSize)
    {
        this.data = new byte[inBufferSize];
    }

    public byte[] Data
    {
        get
        {
            return this.data;
        }
    }

    public int Length
    {
        get;

        set;
    }
}

public class MD5Hasher
{
    private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

    public MD5Hasher()
    {
    }

    public MD5Hasher(int bufferSize)
    {
        this.bufferSize = bufferSize;
    }

    protected int BufferSize
    {
        get
        {
            return this.bufferSize;
        }
    }

    public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
    {
        // Queue with capacity 3x the buffer.
        var fi = new FileInfo(inFile);
        var bigBuffer = new BlockingCollection<IFileBlock>(3);
        var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
        var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

        readFileTask.Start();
        computeHashTask.Start();
        return computeHashTask;
    }

    private static string HashToString(IEnumerable<byte> inHash)
    {
        return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
    }

    private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
    {
        using (var stream = File.OpenRead(fi.FullName))
        {
            int bytesRead;

            do
            {
                var fileBlock = new FileBlock(this.bufferSize);

                fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                bytesRead = fileBlock.Length;

                // Infinite timeout.
                bigBuffer.TryAdd(fileBlock, -1, ct);
            }
            while (bytesRead != 0);

            bigBuffer.CompleteAdding();
        }
    }

    private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
    {
        var fileSize = fi.Length;
        var bytesProcessed = 0L;

        using (HashAlgorithm hashAlgorithm = MD5.Create())
        {
            foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
            {
                hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                bytesProcessed += b.Length;

                // Report progress.
                prg.Report(new ProgressReport(bytesProcessed, fileSize));
            }

            hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
            return HashToString(hashAlgorithm.Hash);
        }
    }
}

public interface IProgressReport
{
    int PercentDone
    {
        get;
    }

    string InfoText
    {
        get;
    }
}

public sealed class ProgressReport : IProgressReport
{
    private const string PercentComplete = "% complete.";

    private readonly int percentDone;

    private readonly string infoText;

    public ProgressReport(long progress, long total)
    {
        this.percentDone = Convert.ToInt32((100 * progress) / total);
        this.infoText = this.PercentDone + PercentComplete;
    }

    public int PercentDone
    {
        get
        {
            return this.percentDone;
        }
    }

    public string InfoText
    {
        get
        {
            return this.infoText;
        }
    }
}

// Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
{
    private readonly SynchronizationContext context;

    private T progress;

    public PropertyProgress(T initialProgress = default(T))
    {
        this.context = SynchronizationContext.Current ?? new SynchronizationContext();
        this.progress = initialProgress;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public T Progress
    {
        get
        {
            return this.progress;
        }

        private set
        {
            this.progress = value;
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
            }
        }
    }

    void IProgress<T>.Report(T value)
    {
        this.context.Post(_ => { this.Progress = value; }, null);
    }
}

}

namespace MD5Library
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;

    public interface IFileBlock
    {
        byte[] Data
        {
            get;
        }

        int Length
        {
            get;

            set;
        }
    }

    public sealed class FileBlock : IFileBlock
    {
        private readonly byte[] data;

        public FileBlock(int inBufferSize)
        {
            this.data = new byte[inBufferSize];
        }

        public byte[] Data
        {
            get
            {
                return this.data;
            }
        }

        public int Length
        {
            get;

            set;
        }
    }

    public class MD5Hasher
    {
        private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

        public MD5Hasher()
        {
        }

        public MD5Hasher(int bufferSize)
        {
            this.bufferSize = bufferSize;
        }

        protected int BufferSize
        {
            get
            {
                return this.bufferSize;
            }
        }

        public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            // Queue with capacity 3x the buffer.
            var fi = new FileInfo(inFile);
            var bigBuffer = new BlockingCollection<IFileBlock>(3);
            var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
            var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

            readFileTask.Start();
            computeHashTask.Start();
            return computeHashTask;
        }

        private static string HashToString(IEnumerable<byte> inHash)
        {
            return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
        }

        private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
        {
            using (var stream = File.OpenRead(fi.FullName))
            {
                int bytesRead;

                do
                {
                    var fileBlock = new FileBlock(this.bufferSize);

                    fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                    bytesRead = fileBlock.Length;

                    // Infinite timeout.
                    bigBuffer.TryAdd(fileBlock, -1, ct);
                }
                while (bytesRead != 0);

                bigBuffer.CompleteAdding();
            }
        }

        private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
        {
            var fileSize = fi.Length;
            var bytesProcessed = 0L;

            using (HashAlgorithm hashAlgorithm = MD5.Create())
            {
                foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                {
                    hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                    bytesProcessed += b.Length;

                    // Report progress.
                    prg.Report(new ProgressReport(bytesProcessed, fileSize));
                }

                hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                return HashToString(hashAlgorithm.Hash);
            }
        }
    }

    public interface IProgressReport
    {
        int PercentDone
        {
            get;
        }

        string InfoText
        {
            get;
        }
    }

    public sealed class ProgressReport : IProgressReport
    {
        private const string PercentComplete = "% complete.";

        private readonly int percentDone;

        private readonly string infoText;

        public ProgressReport(long progress, long total)
        {
            this.percentDone = Convert.ToInt32((100 * progress) / total);
            this.infoText = this.PercentDone + PercentComplete;
        }

        public int PercentDone
        {
            get
            {
                return this.percentDone;
            }
        }

        public string InfoText
        {
            get
            {
                return this.infoText;
            }
        }
    }

    // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
    public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
    {
        private readonly SynchronizationContext context;

        private T progress;

        public PropertyProgress(T initialProgress = default(T))
        {
            this.context = SynchronizationContext.Current ?? new SynchronizationContext();
            this.progress = initialProgress;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public T Progress
        {
            get
            {
                return this.progress;
            }

            private set
            {
                this.progress = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                }
            }
        }

        void IProgress<T>.Report(T value)
        {
            this.context.Post(_ => { this.Progress = value; }, null);
        }
    }
}
Source Link
Jesse C. Slicer
  • 14.1k
  • 1
  • 39
  • 51

Here's how I would go about it (mostly from a readability standpoint rather than a performance one - though the use of immutable data may help contribute to well-measurabl performance and memory usage metrics):

namespace MD5Library { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks;

public interface IFileBlock
{
    byte[] Data
    {
        get;
    }

    int Length
    {
        get;

        set;
    }
}

public sealed class FileBlock : IFileBlock
{
    private readonly byte[] data;

    public FileBlock(int inBufferSize)
    {
        this.data = new byte[inBufferSize];
    }

    public byte[] Data
    {
        get
        {
            return this.data;
        }
    }

    public int Length
    {
        get;

        set;
    }
}

public class MD5Hasher
{
    private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.

    public MD5Hasher()
    {
    }

    public MD5Hasher(int bufferSize)
    {
        this.bufferSize = bufferSize;
    }

    protected int BufferSize
    {
        get
        {
            return this.bufferSize;
        }
    }

    public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
    {
        // Queue with capacity 3x the buffer.
        var fi = new FileInfo(inFile);
        var bigBuffer = new BlockingCollection<IFileBlock>(3);
        var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
        var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));

        readFileTask.Start();
        computeHashTask.Start();
        return computeHashTask;
    }

    private static string HashToString(IEnumerable<byte> inHash)
    {
        return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
    }

    private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
    {
        using (var stream = File.OpenRead(fi.FullName))
        {
            int bytesRead;

            do
            {
                var fileBlock = new FileBlock(this.bufferSize);

                fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                bytesRead = fileBlock.Length;

                // Infinite timeout.
                bigBuffer.TryAdd(fileBlock, -1, ct);
            }
            while (bytesRead != 0);

            bigBuffer.CompleteAdding();
        }
    }

    private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
    {
        var fileSize = fi.Length;
        var bytesProcessed = 0L;

        using (HashAlgorithm hashAlgorithm = MD5.Create())
        {
            foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
            {
                hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                bytesProcessed += b.Length;

                // Report progress.
                prg.Report(new ProgressReport(bytesProcessed, fileSize));
            }

            hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
            return HashToString(hashAlgorithm.Hash);
        }
    }
}

public interface IProgressReport
{
    int PercentDone
    {
        get;
    }

    string InfoText
    {
        get;
    }
}

public sealed class ProgressReport : IProgressReport
{
    private const string PercentComplete = "% complete.";

    private readonly int percentDone;

    private readonly string infoText;

    public ProgressReport(long progress, long total)
    {
        this.percentDone = Convert.ToInt32((100 * progress) / total);
        this.infoText = this.PercentDone + PercentComplete;
    }

    public int PercentDone
    {
        get
        {
            return this.percentDone;
        }
    }

    public string InfoText
    {
        get
        {
            return this.infoText;
        }
    }
}

// Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
{
    private readonly SynchronizationContext context;

    private T progress;

    public PropertyProgress(T initialProgress = default(T))
    {
        this.context = SynchronizationContext.Current ?? new SynchronizationContext();
        this.progress = initialProgress;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public T Progress
    {
        get
        {
            return this.progress;
        }

        private set
        {
            this.progress = value;
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
            }
        }
    }

    void IProgress<T>.Report(T value)
    {
        this.context.Post(_ => { this.Progress = value; }, null);
    }
}

}