Calculating Binary Hashes using TJwFileStreamEx

Why should I use TJwFileStreamEx instead of any other common stream class from the VCL?

Well this question is quite easily answered.

The first thing is that TJwFileStreamEx is based on Memory Mapped Files (MMF). MMF might be the fastest way to access files on your hard disk. Another good reason for using TJwFileStreamEx is its Memory property which is well known from the VCL TMemoryStream class. This property will reduce the effort remarkably.

Getting Started

First you have to include following units to your project:

uses JwsclTypes, JwsclStreams, JwsclCryptProvider;

The signature of our hashing-function looks like this:

function GetFileHash(Algorithm: TJwHashAlgorithm; const Filename: String) : String;

The first parameter is used to specify the Algorithm which is used to hash the file data. The second parameter defines the path to the file which hash you want to compute.

So what do we need to calculate the hash of a file? All we need is an instance of TJwFileStreamEx to open the file and an instance of TJwHash to calculate the hash:

function GetFileHash(Algorithm: TJwHashAlgorithm; const Filename: String) : String;
var
  Stream: TJwFileStreamEx;
  Hash: TJwHash;
begin
  Stream := TJwFileStreamEx.Create(Filename,fmOpenRead);
  try
    Hash := TJwHash.Create(Algorithm);
    // Calculate and return the Hash as a Hex-String
  finally
    Hash.Free;
    Stream.Free;  
  end;
end;

This is the basic framework of our function. To calculate the hash of the file we need 2 methods:

TJwHash.HashData(Data: Pointer; Size: Cardinal);
TJwHash.RetrieveHash(out Len: Cardinal): Pointer;

HashData tells our instance of TJwHash which data is to be hashed and which size this data has. RetrieveHash calculates the Hash and retrieves a pointer to the hash data.

Finally we have to free the Buffer using the class method

TJwHash.FreeBuffer(Buffer: Pointer);

Now our function looks like this:

function GetFileHash(Algorithm: TJwHashAlgorithm; const Filename: String) : String;
var
  Stream: TJwFileStreamEx;
  Hash: TJwHash;
  HashSize: Cardinal;
  HashData: Pointer;
begin
  Stream := TJwFileStreamEx.Create(Filename,fmOpenRead);
  Hash := TJwHash.Create(Algorithm);
  try
    Hash.HashData(Stream.Memory,Stream.Size);
    HashData := Hash.RetrieveHash(HashSize);
    TJwHash.FreeBuffer(HashData);
  finally
    Hash.Free;
    Stream.Free;
  end;
end;

At last we need to convert the binary hash into a hex-string:

for i:= 1 to HashSize do
begin
  Result := Result + IntToHex(PByte(HashData)^,2);
  inc(PByte(HashData));
end;
dec(PByte(HashData),HashSize); //neccessary to free HashData

Now we’re done and finally our function looks like this:

function GetFileHash(Algorithm: TJwHashAlgorithm; const Filename: String) : String;
var
  Stream: TJwFileStreamEx;
  Hash: TJwHash;
  HashSize: Cardinal;
  HashData: Pointer;
  i: Integer;
begin
  Stream := TJwFileStreamEx.Create(Filename,fmOpenRead);
  Hash := TJwHash.Create(Algorithm);
  try
    Hash.HashData(Stream.Memory,Stream.Size);
    HashData := Hash.RetrieveHash(HashSize);

    for i:= 1 to HashSize do
    begin
      Result := Result + IntToHex(PByte(HashData)^,2);
      inc(PByte(HashData));
    end;
    dec(PByte(HashData),HashSize);

    TJwHash.FreeBuffer(HashData);
  finally
    Hash.Free;
    Stream.Free;
  end;
end;

How do I use this function?

// first param can be one of these values:
// haMD2, haMD4, haMD5, haSHA
Label1.Caption := GetFileHash(haSHA,‘C:\Progam Files\Program\AFile.ext’);

OK, but what is the advantage of TJwFileStreamEx over other Streams?

Well, you don’t have to load the file into a TMemoryStream and thus the file will not be loaded completely into memory. Furthermore this solution is much faster than a solution using TMemoryStream and similar…

Download

Download the source and binaries of this example (260kiB). This includes the new and necessary JWSCL source files. However you need to download the whole package separately.