how to convert big-endian numbers to native numbers delphi - delphi

I want to know how to convert big-endian numbers to native numbers in Delphi. I am porting some C++ code in that I came across:
unsigned long blockLength = *blockLengthPtr++ << 24;
blockLength |= *blockLengthPtr++ << 16;
blockLength |= *blockLengthPtr++ << 8;
blockLength |= *blockLengthPtr;
unsigned long dataLength = *dataLengthPtr++ << 24;
dataLength |= *dataLengthPtr++ << 16;
dataLength |= *dataLengthPtr++ << 8;
dataLength |= *dataLengthPtr;
I am not familiar with C++, so I don't understand what those operators do.

Andreas's answer is a pretty good example of how to do it in pure pascal, but it still looks kinda awkward, just like the C++ code. This can actually be done in a single assembly instruction, though which one depends on whether you're using 32-bit or 16-bit integers:
function SwapEndian32(Value: integer): integer; register;
asm
bswap eax
end;
function SwapEndian16(Value: smallint): smallint; register;
asm
rol ax, 8
end;

To reverse the order of the bits:
procedure SwapEndiannessOfBits(var Value: cardinal);
var
tmp: cardinal;
i: Integer;
begin
tmp := 0;
for i := 0 to 8*sizeof(Value) - 1 do
inc(tmp, ((Value shr i) and $1) shl (8*sizeof(Value) - i - 1));
Value := tmp;
end;
To reverse the order of the bytes:
procedure SwapEndiannessOfBytes(var Value: cardinal);
var
tmp: cardinal;
i: Integer;
begin
tmp := 0;
for i := 0 to sizeof(Value) - 1 do
inc(tmp, ((Value shr (8*i)) and $FF) shl (8*(sizeof(Value) - i - 1)));
Value := tmp;
end;
I think the last one is what you are looking for. Most likely there are faster and more elegant solutions, though.
Disclaimer: I might be totally wrong. I feel a bit confused at the moment. Hopefully someone else will see this question and provide a more definite answer!

Related

Swapping order of bytes in Delphi

I'm not very familiar with arrays of bite and big/little endians but I need to write an integer value into byte array in reverse and I don't know how to do it in Delphi code. C# has BitConverter.Reverse methong which is so much easier, is there any equivalent for it in Delphi?
This is my code so far:
x := 1500977838953;
setLength(byteArray, 8);
Move(x, byteArray[2], SizeOf(x));
showMessage(ByteToHex(byteArray));
ByteToHex is a method that returns me hex string so I can read the bytes if they are in correct order. The result that I am getting is : 0000693B40795D01 but I need it to be: 00-00-01-5D-79-40-3B-69
Any ideas how I can achieve this?
Edit:
function ByteToHex(b: array of byte): String;
const HexSymbols = '0123456789ABCDEF';
var i: integer;
begin
SetLength(Result, 2*Length(b));
for i := 0 to Length(b)-1 do begin
Result[1 + 2*i + 0] := HexSymbols[1 + b[i] shr 4];
Result[1 + 2*i + 1] := HexSymbols[1 + b[i] and $0F];
end;
end;
Here is an example how to use the ReverseBytes() procedure:
program Project20;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure ReverseBytes(Source, Dest: Pointer; Size: Integer);
begin
Dest := PByte(NativeUInt(Dest) + Size - 1);
while (Size > 0) do
begin
PByte(Dest)^ := PByte(Source)^;
Inc(PByte(Source));
Dec(PByte(Dest));
Dec(Size);
end;
end;
var x,y : Int64;
begin
x := 1500977838953;
WriteLn(x);
ReverseBytes(Addr(x),Addr(y),SizeOf(x)); // Or ReverseBytes(#x,#y,SizeOf(x));
WriteLn(IntToHex(x));
WriteLn(IntToHex(y));
ReadLn;
end.
Output:
1500977838953
0000015D79403B69
693B40795D010000
To get the address of a variable, use the Addr() function or the # operator.
The result is a 64-bit integer with all bytes in reversed order, as shown by the output.
There are other ways to swap the byte order of a variable. Search for bswap for example.

Color to ARGB Convert

I have an integer color values like -16447153. And how to convert it ARGB?
In c#, he used color function,
Color.FromArgb(buf[buf_pos])
is there a function in delphi as like above otherwise how can i convert ?
I tried like this:
procedure TJP2RenderPreview.put_region(size : _Ckdu_coords; buf : array of Integer; offX, offY : Integer);
var
x, y : Integer;
width, height : Integer;
buf_pos : Integer;
RowPtr : PRGBQuad;
begin
width:= size.__property_get_x;
height:= size.__property_get_y;
buf_pos:=0;
for y := offY to offY + height - 1 do
begin
RowPtr:= formMain.imgPreview.Bitmap.ScanLine[y];
Inc(RowPtr, offX);
for x := 0 to width - 1 do
begin
RowPtr.rgbReserved:= (buf[buf_pos] div $1000000);
RowPtr.rgbRed:= ((buf[buf_pos] mod $1000000) div $10000);
RowPtr.rgbGreen:= ((buf[buf_pos] mod $10000) div $100);
RowPtr.rgbBlue:= (buf[buf_pos] mod $100);
Inc(RowPtr);
Inc(buf_pos);
end;
end;
formMain.imgPreview.Refresh;
formMain.imgPreview.Update;
end;
But this is not true, it seems grey :/
You appear to have got your channels mixed up. You are treating the input as RGBA when you say it is ARGB. In any case, your code using div and mod is inefficent. Bitwise shifting is the idiomatic way.
You can do it like this for ARGB input:
procedure ARGBtoColorChannels(ARGB: DWORD; out A, R, G, B: Byte);
begin
A := Byte(ARGB);
R := Byte(ARGB shr 8);
G := Byte(ARGB shr 16);
B := Byte(ARGB shr 24);
end;
Or if you start with RGBA then it goes like this:
procedure RGBAtoColorChannels(RGBA: DWORD; out A, R, G, B: Byte);
begin
R := Byte(RGBA);
G := Byte(RGBA shr 8);
B := Byte(RGBA shr 16);
A := Byte(RGBA shr 24);
end;
And these are little endian versions, since Delphi only targets on little endian machines, at the time of writing.
If you are looking for a function to convert ARGB to RGBA then you can make one like so:
function ARGBtoRGBA(ARGB: DWORD): DWORD;
var
A, R, G, B: Byte;
begin
ARGBtoColorChannels(ARGB, A, R, G, B);
Result := R or (G shl 8) or (B shl 16) or (A shl 24);
end;
And for completeness the reverse is:
function RGBAtoARGB(RGBA: DWORD): DWORD;
var
A, R, G, B: Byte;
begin
RGBAtoColorChannels(RGBA, A, R, G, B);
Result := A or (R shl 8) or (G shl 16) or (B shl 24);
end;
In order for you to know what to do, you need to understand what buf is. Is it ARGB or RGBA? Assuming it it ARGB, which seems likely given the content of your question, you are attempting to copy to RowPtr, which also appears to be ARGB. In which case you can blit the color like this:
PInteger(RowPtr)^ := buf[buf_pos];
I would also strongly recommend that you change the type of the buf parameter. An unsigned type is better here, because you may need to perform bitwise operations. What's more you are copying the entire array via the stack which is very inefficient. For large bitmaps you will suffer stack overflow. Instead that parameter should be:
const buf: array of DWORD;
or
const buf: array of Cardinal;
Using const means that a reference to the array is passed, rather than a copy.
You may as well avoid using PRGBQuad and declare RowPtr to be of type PCardinal, say.
Then you can assign like this:
RowPtr^ := buf[buf_pos];
At that point you don't need to do it pixel by pixel. You can copy an entire scanline with Move because this is a simply blit.
for y := offY to offY + height - 1 do
begin
RowPtr := formMain.imgPreview.Bitmap.ScanLine[y];
Inc(RowPtr, offX);
Move(buf[buf_pos], RowPtr^, width*SizeOf(RowPtr^));
Inc(buf_pos, width);
end;
Calling Refresh and Update seems pointless. I'm not sure you need to call either, but if you do, just call Invalidate instead!

how to improve the code (Delphi) for loading and searching in a dictionary?

I'm a Delphi programmer.
I have made a program who uses dictionaries with words and expressions (loaded in program as "array of string").
It uses a search algorithm based on their "checksum" (I hope this is the correct word).
A string is transformed in integer based on this:
var
FHashSize: Integer; //stores the value of GetHashSize
HashTable, HashTableNoCase: array[Byte] of Longword;
HashTableInit: Boolean = False;
const
AnsiLowCaseLookup: array[AnsiChar] of AnsiChar = (
#$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07,
#$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
#$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17,
#$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
#$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27,
#$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
#$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37,
#$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
#$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
#$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
#$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F,
#$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
#$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
#$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
#$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87,
#$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
#$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97,
#$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
#$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7,
#$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
#$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7,
#$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF,
#$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7,
#$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
#$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7,
#$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
#$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7,
#$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
#$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7,
#$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF);
implementation
function GetHashSize(const Count: Integer): Integer;
begin
if Count < 65 then
Result := 256
else
Result := Round(IntPower(16, Ceil(Log10(Count div 4) / Log10(16))));
end;
function Hash(const Hash: LongWord; const Buf; const BufSize: Integer): LongWord;
var P: PByte;
I: Integer;
begin
P := #Buf;
Result := Hash;
for I := 1 to BufSize do
begin
Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
Inc(P);
end;
end;
function HashStrBuf(const StrBuf: Pointer; const StrLength: Integer; const Slots: LongWord): LongWord;
var P: PChar;
I, J: Integer;
begin
if not HashTableInit then
InitHashTable;
P := StrBuf;
if StrLength <= 48 then // Hash all characters for short strings
Result := Hash($FFFFFFFF, P^, StrLength)
else
begin
// Hash first 16 bytes
Result := Hash($FFFFFFFF, P^, 16);
// Hash last 16 bytes
Inc(P, StrLength - 16);
Result := Hash(Result, P^, 16);
// Hash 16 bytes sampled from rest of string
I := (StrLength - 48) div 16;
P := StrBuf;
Inc(P, 16);
for J := 1 to 16 do
begin
Result := HashTable[Byte(Result) xor Byte(P^)] xor (Result shr 8);
Inc(P, I + 1);
end;
end;
// Mod into slots
if Slots <> 0 then
Result := Result mod Slots;
end;
procedure InitHashTable;
var I, J: Byte;
R: LongWord;
begin
for I := $00 to $FF do
begin
R := I;
for J := 8 downto 1 do
if R and 1 <> 0 then
R := (R shr 1) xor $EDB88320
else
R := R shr 1;
HashTable[I] := R;
end;
Move(HashTable, HashTableNoCase, Sizeof(HashTable));
for I := Ord('A') to Ord('Z') do
HashTableNoCase[I] := HashTableNoCase[I or 32];
HashTableInit := True;
end;
The result of the HashStrBuf is "and (FHashSize - 1)" and is used as index in an "array of array of Integer" (of FHashSize size) to store the index of the string from that "array of string".
This way, when searches for a string, it's transformed in "checksum" and then the code searches in the "branch" with this index comparing this string with the strings from dictionary who have the same "checksum".
Ideally each string from dictionary should have unique checksum. But in the "real world" about 2/3 share the same "checksum" with other words. Because of that the search is not that fast.
In these dictionaries strings are composed of this characters: ['a'..'z',#224..#246,#248..#254,#154,#156..#159,#179,#186,#191,#190,#185,'0'..'9', '''']
Is there any way to improve the "hashing" so the strings would have more unique "checksums"?
Oh, one way is to increase the size of that "array of array of Integer" (FHashSize) but it cannot be increased too much because it takes a lot of Ram.
Another thing: these dictionaries are stored on HDD only as words/expressions (not the "checksums"). Their "checksum" is generated at program startup. But it takes a lot of seconds to do that...
Is there any way to speed up the startup of the program? Maybe by improving the "hashing" function, maybe by storing the "checksums" on HDD and loading them from there...
Any input would be appreciated...
PS: here is the code to search:
function TDictionary.LocateKey(const Key: AnsiString): Integer;
var i, j, l, H: Integer;
P, Q: PChar;
begin
Result := -1;
l := Length(Key);
H := HashStrBuf(#Key[1], l, 0) and (FHashSize - 1);
P := #Key[1];
for i := 0 to High(FHash[H]) do //FHash is that "array of array of integer"
begin
if l <> FKeys.ItemSize[FHash[H][i]] then //FKeys.ItemSize is an byte array with the lengths of strings from dictionary
Continue;
Q := FKeys.Pointer(FHash[H][i]); //pointer to string in dictionary
for j := 0 to l - 1 do
if (P + j)^ <> (Q + j)^ then
Break;
if j = l then
begin
Result := FHash[H][i];
Exit;
end;
end;
end;
Don't reinvent the wheel!
IMHO your hashing is far from efficient, and your collision algorithm can be improved.
Take a look for instance at the IniFiles unit, and the THashedStringList.
It's a bit old, but a good start for a string list using hashes.
There are a lot of good Delphi implementation of such, like in SuperObject and a lot of other code...
Take a look at our SynBigTable unit, which can handle arrays of data in memory or in file very fast, with full indexed searches. Or our latest TDynArray wrapper around any dynamic array of data, to implement TList-like methods to it, including fast binary search. I'm quite sure it could be faster than your hand-tuned code using hashing, if you use an ordered index then fast binary search.
Post-Scriptum:
About pure hashing speed of a string content, take a look at this function - rename RawByteString into AnsiString, PPtrInt into PPointer, and PtrInt into Integer for Delphi 7:
function Hash32(const Text: RawByteString): cardinal;
function SubHash(P: PCardinalArray): cardinal;
{$ifdef HASINLINE}inline;{$endif}
var s1,s2: cardinal;
i, L: PtrInt;
const Mask: array[0..3] of cardinal = (0,$ff,$ffff,$ffffff);
begin
if P<>nil then begin
L := PPtrInt(PtrInt(P)-4)^; // fast lenght(Text)
s1 := 0;
s2 := 0;
for i := 1 to L shr 4 do begin // 16 bytes (4 DWORD) by loop - aligned read
inc(s1,P^[0]);
inc(s2,s1);
inc(s1,P^[1]);
inc(s2,s1);
inc(s1,P^[2]);
inc(s2,s1);
inc(s1,P^[3]);
inc(s2,s1);
inc(PtrUInt(P),16);
end;
for i := 1 to (L shr 2)and 3 do begin // 4 bytes (DWORD) by loop
inc(s1,P^[0]);
inc(s2,s1);
inc(PtrUInt(P),4);
end;
inc(s1,P^[0] and Mask[L and 3]); // remaining 0..3 bytes
inc(s2,s1);
result := s1 xor (s2 shl 16);
end else
result := 0;
end;
begin // use a sub function for better code generation under Delphi
result := SubHash(pointer(Text));
end;
There is even a pure asm version, even faster, in our SynCommons.pas unit. I don't know any faster hashing function around (it's faster than crc32/adler32/IniFiles.hash...). It's based on adler32, but use DWORD aligned reading and summing for even better speed. This could be improved with SSE asm, of course, but here is a fast pure Delphi hash function.
Then don't forget to use "multiplication"/"binary and operation" for hash resolution, just like in IniFiles. It will reduce the number of iteration to your list of hashs.
But since you didn't provide the search source code, we are not able to know what could be improved here.
If you are using Delphi 7, consider using Julian Bucknall's lovely Delphi data types code, EzDsl (Easy Data Structures Library).
Now you don't have to reinvent the wheel as another wise person has also said.
You can download ezdsl, a version that I have made work with both Delphi 7, and recent unicode delphi versions, here.
In particular the unit name EHash contains a hash table implementation, which has various hashing algorithms plug-inable, or you can write your own plugin function that just does the hashing function of your choice.
As a word to the wise, if you are using a Unicode Delphi version; I would be careful about hashing your unicode strings with a code library like this, without checking how its hashing algorithms perform on your system. The OP here is using Delphi 7, so Unicode is not a factor for the original question.
I think you'll find a database (without checksums) a lot quicker. Maybe try sqlite which will give you a single file database. There are many Delphi Libraries available.

CRC-CCITT (0xFFFF) function?

Can someone help me with Delphi implementation of CRC-CCITT (0xFFFF)?
Already get the Java version, but confusing on how to port it to Delphi
public static int CRC16CCITT(byte[] bytes) {
int crc = 0xFFFF; // initial value
int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
for (byte b : bytes) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >> (7-i) & 1) == 1);
boolean c15 = ((crc >> 15 & 1) == 1);
crc <<= 1;
if (c15 ^ bit) crc ^= polynomial;
}
}
crc &= 0xffff;
//System.out.println("CRC16-CCITT = " + Integer.toHexString(crc));
return crc;
}
and for PHP implementation
<?php
function crc16($data)
{
$crc = 0xFFFF;
for ($i = 0; $i < strlen($data); $i++)
{
$x = (($crc >> 8) ^ ord($data[$i])) & 0xFF;
$x ^= $x >> 4;
$crc = (($crc << 8) ^ ($x << 12) ^ ($x << 5) ^ $x) & 0xFFFF;
}
return $crc;
}
0xFFFF translates to $FFFF
& translates to and
^ translates to xor
<< translates to shl
>> translates to shr
x ^= y translates to x := x xor y, similar for &=, <<=, etc.
These operators generally have higher precedence in Delphi so they usually need to have their arguments parenthesized.
I'm quite sure that there are plenty of other implementations of CRC16 etc. for Delphi, see e.g. Improve speed on Crc16 calculation
function CRC16CCITT(bytes: TBytes): Word;
const
polynomial = $1021; // 0001 0000 0010 0001 (0, 5, 12)
var
crc: Word;
I, J: Integer;
b: Byte;
bit, c15: Boolean;
begin
crc := $FFFF; // initial value
for I := 0 to High(bytes) do
begin
b := bytes[I];
for J := 0 to 7 do
begin
bit := (((b shr (7-J)) and 1) = 1);
c15 := (((crc shr 15) and 1) = 1);
crc := crc shl 1;
if ((c15 xor bit) <> 0) then crc := crc xor polynomial;
end;
end;
Result := crc and $ffff;
end;
You can find one in Delphi Encryption Compendium (DEC) component.
5 Checksums (CRC32, CRC16-CCITT, CRC16-Standard ...)
http://blog.digivendo.com/2008/11/delphi-encryption-compendium-dec-52-for-d2009-released/
i found some code that works:
function crc16(Buffer:String;Polynom,Initial:Cardinal):Cardinal;
var
i,j: Integer;
begin
Result:=Initial;
for i:=1 to Length(Buffer) do begin
Result:=Result xor (ord(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;
source : http://www.miscel.dk/MiscEl/CRCcalculations.html
unit CRC16CCITT;
interface
function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word;
implementation
const
crc16_table: array [0..$FF] of word = (0,4489,8978,12955,17956,22445,25910,29887,35912,40385,44890,48851,51820,56293,59774,
63735,4225,264,13203,8730,22181,18220,30135,25662,40137,36160,49115,44626,56045,52068,63999,
59510,8450,12427,528,5017,26406,30383,17460,21949,44362,48323,36440,40913,60270,64231,51324,
55797,12675,8202,4753,792,30631,26158,21685,17724,48587,44098,40665,36688,64495,60006,55549,
51572,16900,21389,24854,28831,1056,5545,10034,14011,52812,57285,60766,64727,34920,39393,43898,
47859,21125,17164,29079,24606,5281,1320,14259,9786,57037,53060,64991,60502,39145,35168,48123,
43634,25350,29327,16404,20893,9506,13483,1584,6073,61262,65223,52316,56789,43370,47331,35448,
39921,29575,25102,20629,16668,13731,9258,5809,1848,65487,60998,56541,52564,47595,43106,39673,
35696,33800,38273,42778,46739,49708,54181,57662,61623,2112,6601,11090,15067,20068,24557,28022,
31999,38025,34048,47003,42514,53933,49956,61887,57398,6337,2376,15315,10842,24293,20332,32247,
27774,42250,46211,34328,38801,58158,62119,49212,53685,10562,14539,2640,7129,28518,32495,19572,
24061,46475,41986,38553,34576,62383,57894,53437,49460,14787,10314,6865,2904,32743,28270,23797,
19836,50700,55173,58654,62615,32808,37281,41786,45747,19012,23501,26966,30943,3168,7657,12146,
16123,54925,50948,62879,58390,37033,33056,46011,41522,23237,19276,31191,26718,7393,3432,16371,
11898,59150,63111,50204,54677,41258,45219,33336,37809,27462,31439,18516,23005,11618,15595,3696,
8185,63375,58886,54429,50452,45483,40994,37561,33584,31687,27214,22741,18780,15843,11370,7921,
3960);
function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word;
var
i : integer;
begin
for i := 0 to len-1 do
crc := (crc shr 8) xor crc16_table[(crc xor data[i]) and $ff];
result := crc;
end;
end.

Improve speed on Crc16 calculation

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;
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)
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/
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.
Old thread, i know. Here is my implementation (just one loop):
function crc16( s : string; bSumPos : Boolean = FALSE ) : Word;
var
L, crc, sum, i, x, j : Word;
begin
Result:=0;
L:=length(s);
if( L > 0 ) then
begin
crc:=$FFFF;
sum:=length(s);
for i:=1 to L do
begin
j:=ord(s[i]);
sum:=sum+((i) * j);
x:=((crc shr 8) xor j) and $FF;
x:=x xor (x shr 4);
crc:=((crc shl 8) xor (x shl 12) xor (x shl 5) xor x) and $FFFF;
end;
Result:=crc+(Byte(bSumPos) * sum);
end;
end;
Nice thing is also that you can create an unique id with it, for example to get an unique identifier for a filename, like:
function uniqueId( s : string ) : Word;
begin
Result:=crc16( s, TRUE );
end;
Cheers,
Erwin Haantjes

Resources