views:

146

answers:

3

I need to calculate Crc16 checksums with a $1021 polynom over large files, below is my current implementation but it's rather slow on large files (eg a 90 MB file takes about 9 seconds).

So my question is how to improve my current implementation (to make it faster), I have googled and looked at some samples implementing a table lookup but my problem is that I don't understand how to modify them to include the polynom (probably my math is failing).

{ based on http://miscel.dk/MiscEl/CRCcalculations.html }
function Crc16(const Buffer: PByte; const BufSize: Int64;
  const Polynom: WORD=$1021; const Seed: WORD=0): Word;
var
  i,j: Integer;
begin
  Result := Seed;

  for i:=0 to BufSize-1 do
  begin
    Result := Result xor (Buffer[i] shl 8);

    for j:=0 to 7 do begin
      if (Result and $8000) <> 0 then
        Result := (Result shl 1) xor Polynom
      else Result := Result shl 1;
    end;
  end;

  Result := Result and $FFFF;
end;
+6  A: 

If you want this to be fast, you need to implement a table-lookup CRC algorithm.

See chapter 10 of A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS INDEX V3.00 (9/24/96)

Ira Baxter
+1 Good article, it helps for understanding how the table lookup works
Remko
+2  A: 

Your Result variable is a Word, which means there are 64k possible values it could have upon entry to the inner loop. Calculate the 64k possible results that the loop could generate and store them in an array. Then, instead of looping eight times for each byte of the input buffer, simply look up the next value of the checksum in the array. Something like this:

function Crc16(const Buffer: PByte; const BufSize: Int64;
  const Polynom: Word = $1021; const Seed: Word = 0): Word;
{$J+}
const
  Results: array of Word = nil;
  OldPolynom: Word = 0;
{$J-}
var
  i, j: Integer;
begin
  if (Polynom <> OldPolynom) or not Assigned(Results) then begin
    SetLength(Results, 65535);
    for i := 0 to Pred(Length(Results)) do begin
      Results[i] := i;
      for j := 0 to 7 do
        if (Results[i] and $8000) <> 0 then
          Results[i] := (Results[i] shl 1) xor Polynom
        else
          Results[i] := Results[i] shl 1;
    end;
    OldPolynom := Polynom;
  end;

  Result := Seed;
  for i := 0 to Pred(BufSize) do
    Result := Results[Result xor (Buffer[i] shl 8)];
end;

That code recalculates the lookup table any time Polynom changes. If that parameter varies among a set of values, then consider caching the lookup tables you generate for them so you don't waste time calculating the same tables repeatedly.

If Polynom will always be $1021, then don't even bother having a parameter for it. Calculate all 64k values in advance and hard-code them in a big array, so your entire function is reduced to just the last three lines of my function above.

Rob Kennedy
Rob, I just copy/pasted your code to test and for a short teststring the results are the same as my function. But when running it on the large file the results are different. Need to test more to see why
Remko
Rob, your table is different from the table from Jcl, first row of Jcl Table: (0, $1021, $2042, $3063, $4084, $50A5, $60C6, $70E7, $8108, $9129, $A14A, $B16B, $C18C, $D1AD, $E1CE, First row of your table: (0, $100, $200, $300, $400, $500, $600, $700, $800, $900, $A00,
Remko
+2  A: 

Look for CRC routines from jclMath.pas unit of Jedi Code Library. It uses CRC lookup tables.

http://jcl.svn.sourceforge.net/viewvc/jcl/trunk/jcl/source/common/

Andrei K.
Jcl implementation is producing the same results as my function (when I init the table with the correct polynom) and is quite fast (421 ms with the same large file)
Remko
I accepted this answer because Jcl has a Delphi implementation using lookup table. I found Ira Baxter's answer helpfull because of the explanation in the article and also thanks to Rob for his answer because there are some usefull bits in there.
Remko