{***************************************************************************** The DEC team (see file NOTICE.txt) licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. A copy of this licence is found in the root directory of this project in the file LICENCE.txt or alternatively at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *****************************************************************************} { Implementation of threadsafe CRC checksum functions. The following standard CRCs are supported: CRC-8, CRC-10, CRC-12 (Mobil Telephone), CRC-16, CRC-16-CCITT, CRC-16-ZModem, CRC-24 (PGP's MIME64 Armor CRC), CRC-32, CRC-32-CCITT and CRC-32-ZModem. How to use: var CRC16: UInt16; begin CRC16 := CRCCalc(CRC_16, Data, SizeOf(Data)); // all in one end; or var CRC: TCRCDef; CRC32: UInt32; begin CRCInit(CRC, CRC_32); // setup CRC data structure CRCCode(CRC, Data, SizeOf(Data)); // calcs CRC for "Data" CRCCode(CRC, PChar(string)^, Length(string) * SizeOf(string[1])); // calcs CRC for String CRC32 := CRCDone(CRC); // returns correct combined CRC for Data and String // after CRCDone we can start a new calculation end; } unit DECCRC; interface {$INCLUDE DECOptions.inc} type /// /// CRC Definition Structure /// PCRCDef = ^TCRCDef; /// /// Record with meta data about a single CRC algorithm/polynom /// Do *not* reorder or change this structure /// /// SizeOf(TCRCDef) = 1056 = 0420h /// /// TCRCDef = packed record /// /// Lookup Table, precomputed in CRCSetup /// Table : array[0..255] of UInt32; /// /// Intermediate CRC /// CRC : UInt32; /// /// Is this Polynomial an inverse function? /// Inverse : LongBool; /// /// Shift Value for CRCCode (for more speed) /// Shift : UInt32; /// /// Start Value of CRC cComputation /// InitVector : UInt32; /// /// Final XOR Vector of computed CRC /// FinalVector : UInt32; /// /// Precomputed AND Mask of computed CRC /// Mask : UInt32; /// /// Bitsize of CRC /// Bits : UInt32; /// /// Used Polynomial /// Polynomial : UInt32; end; /// /// predefined standard CRC Types /// TCRCType = ( CRC_8, CRC_10, CRC_12, CRC_16, CRC_16CCITT, CRC_16XModem, CRC_24, CRC_32, CRC_32CCITT, CRC_32ZModem, CRC_8ATMHEC, CRC_8SMBus, CRC_15CAN, CRC_16ZMODEM ); type /// /// Callback method used by some CRC calculation routines to fetch the data /// to be processed /// /// /// Buffer containing the data to be processed /// /// /// Number of bytes of the buffer to be processed /// /// /// /// TReadMethod = function(var Buffer; Count: Int64): Int64 of object; // initialize CRC Definition with a custom Algorithm /// /// Fills the individual fields of a CRC meta data structure /// /// /// Structure whose fields shall be filled /// /// /// CRC polynome, defining the algorithm /// /// /// Size of the CRC value to be computed in bits. Needs to be at least 8 /// /// /// Initial value for the vector going into each calculation cycle /// /// /// Final XOR Vector of computed CRC /// /// /// true if this Polynomial is an inverse function /// /// /// true on success, false when a number smaller 8 is being passed as Bits parameter /// function CRCSetup(var CRCDef: TCRCDef; Polynomial, Bits, InitVector, FinalVector: UInt32; Inverse: LongBool): Boolean; /// /// Retrieves the necessary meta data and precomputed tables for a given CRC /// algorithm. /// /// /// Record in which the to be retrieved meta data will be returned /// /// /// Specifies the exact CRC type which shall be initialized /// /// /// true on success /// function CRCInit(var CRCDef: TCRCDef; CRCType: TCRCType): Boolean; /// /// Calculate the CRC of the contents of the passed in buffer. /// /// /// Structure with the necessary metadata for the CRC algorithm to be used. /// CRC processing state is being updated during calculation to enable this /// structure to be fed in another call to CRCCode if a CRC over multiple /// buffers has to be calculated. /// /// /// Buffer with the data the CRC shall be calculated from /// /// /// Number of bytes to calculate the CRC from, starting at the beginning of /// the buffer /// /// /// Calculated CRC value, including any necessary correction (like CRCDone). /// CRCDef.CRC holds the actual computed CRC, additional calls of CRCCode /// compute the total CRC of split buffers /// function CRCCode(var CRCDef: TCRCDef; const Buffer; Size: UInt32): UInt32; overload; /// /// Calculate the CRC of the contents provided by a given callback /// /// /// Structure with the necessary metadata for the CRC algorithm to be used. /// CRC processing state is being updated during calculation to enable this /// structure to be fed in another call to CRCCode if a CRC over multiple /// buffers has to be calculated. /// /// /// Callback which is being called to get the data the CRC is processed over, /// e.g. TStream.Read /// /// /// Number of bytes over which the CRC will be calculated. The callback will /// be called until that number of bytes have been processed. /// /// /// Calculated CRC value, including any necessary correction (like CRCDone). /// CRCDef.CRC holds the actual computed CRC, additional calls of CRCCode /// compute the total CRC of split buffers /// function CRCCode(var CRCDef: TCRCDef; ReadMethod: TReadMethod; Size: UInt32 = $FFFFFFFF): UInt32; overload; { TODO : DUnitTests für die Callback-Methoden Varianten von CRCCode und CRCCalc schreiben } // // CRCInit(CRC, CRC_32); // setup CRC data structure // CRCCode(CRC, Data, SizeOf(Data)); // calcs CRC for "Data" // CRCCode(CRC, PChar(string)^, Length(string) * SizeOf(string[1])); // calcs CRC for String // CRC32 := CRCDone(CRC); // returns corrected CRC as definied in CRCDef and resets CRCDef.CRC to InitVector /// /// Corrects the CRC via the final vector and resets the internal intermediate /// CRC value to the init vector so the next CRC calculation can start. /// /// /// Structure with the current CRC state /// /// /// Final CRC value /// function CRCDone(var CRCDef: TCRCDef): UInt32; /// /// Calculates a CRC over some Buffer with Size Bytes length. Processing is /// being done in one single step /// /// /// Specifies the CRC algorithm to be used /// /// /// Buffer with the data to calculate the CRC from /// /// /// Number of bytes over which the CRC will be calculated from the beginning /// of the buffer /// function CRCCalc(CRCType: TCRCType; const Buffer; Size: UInt32): UInt32; overload; /// /// Calculates a CRC. Data is passed via callback, which is called repeatedly /// if necessary /// /// /// Specifies the CRC algorithm to be used /// /// /// Callback which is being called to get the data the CRC is processed over /// e.g. TStream.Read /// /// /// Number of bytes over which the CRC will be calculated. The callback will /// be called until that number of bytes have been processed. /// /// /// Calculated CRC value. /// function CRCCalc(CRCType : TCRCType; ReadMethod : TReadMethod; Size : UInt32 = $FFFFFFFF): UInt32; overload; /// /// Calculates a CRC according a predefined CRC16-Standard over some Buffer /// with Size Bytes length. Processing is being done in one single step /// /// /// call CRC := CRC16(0, Data, SizeOf(Data)); /// /// /// Specifies the CRC algorithm to be used /// /// /// Buffer with the data to calculate the CRC from /// /// /// Number of bytes over which the CRC will be calculated from the beginning /// of the buffer /// /// /// Calculated CRC16 value /// function CRC16(CRC: UInt16; const Buffer; Size: UInt32): UInt16; /// /// Calculates a CRC according the CRC32-CCITT standard over some Buffer /// with Size Bytes length. Processing is being done in one single step /// /// /// call CRC := CRC32(0, Data, SizeOf(Data)); /// /// /// Specifies the CRC algorithm to be used /// /// /// Buffer with the data to calculate the CRC from /// /// /// Number of bytes over which the CRC will be calculated from the beginning /// of the buffer /// /// /// Calculated CRC32 value /// function CRC32(CRC: UInt32; const Buffer; Size: UInt32): UInt32; implementation {$IFOPT Q+}{$DEFINE RESTORE_OVERFLOWCHECKS}{$Q-}{$ENDIF} {$IFOPT R+}{$DEFINE RESTORE_RANGECHECKS}{$R-}{$ENDIF} type PCRCTab = ^TCRCTab; /// /// Array type for the meta data definitions of the individual CRC algorithms /// TCRCTab = array[TCRCType] of packed record Poly, Bits, Init, FInit: UInt32; Inverse: LongBool; end; const /// /// Table containing meta data of various well known CRC algorithms/polynoms /// CRCTab : TCRCTab = ( (Poly: $000000D1; Bits: 08; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_8 GSM/ERR (Poly: $00000233; Bits: 10; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_10 ATM/OAM Cell (Poly: $0000080F; Bits: 12; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_12 (Poly: $00008005; Bits: 16; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_16 ARC;IBM;MODBUS RTU // Init value of 1D0F instead of FFFF because the code doesn't fill with zeros, // which would otherwise be required for the CCITT variant (Poly: $00001021; Bits: 16; Init: $00001D0F; FInit: $00000000; Inverse: False), // CRC_16 CCITT ITU (Poly: $00008408; Bits: 16; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_16 XModem (Poly: $00864CFB; Bits: 24; Init: $00B704CE; FInit: $00000000; Inverse: False), // CRC_24 (Poly: $9DB11213; Bits: 32; Init: $FFFFFFFF; FInit: $FFFFFFFF; Inverse: True), // CRC_32 (Poly: $04C11DB7; Bits: 32; Init: $FFFFFFFF; FInit: $FFFFFFFF; Inverse: True), // CRC_32CCITT (Poly: $04C11DB7; Bits: 32; Init: $FFFFFFFF; FInit: $00000000; Inverse: True), // CRC_32ZModem (Poly: $00000007; Bits: 08; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_8ATMHEC (Poly: $00000007; Bits: 08; Init: $00000000; FInit: $00000000; Inverse: False), // CRC_8SMBus (Poly: $00004599; Bits: 15; Init: $00000000; FInit: $00000000; Inverse: True), // CRC_15CAN (Poly: $00001021; Bits: 16; Init: $00000000; FInit: $00000000; Inverse: False) // CRC_16ZMODEM ); // some other CRC's, not all yet verfied // DD $00001021, 16, $0000FFFF, $00000000, 0 // CRC_16 CCITT British Aerospace // DD $00004003, 16, $00000000, $00000000, -1 // CRC_16 reversed // DD $00001005, 16, $00000000, $00000000, -1 // CRC_16 X25 // https://fenix.tecnico.ulisboa.pt/downloadFile/3779571246541/BasicCrd.pdf enthält // eine beschreibung dieser BasicCard Smartcard incl. C-CRC Quellcode, aber die // Polynome konnte ich so noch nicht überprüfen // DD $00000053, 16, $00000000, $00000000, -1 // BasicCard 16Bit CRC (sparse poly for Crypto MCU) // DD $000000C5, 32, $00000000, $00000000, -1 // BasicCard 32Bit CRC function CRCSetup(var CRCDef: TCRCDef; Polynomial, Bits, InitVector, FinalVector: UInt32; Inverse: LongBool): Boolean; // initialize CRCDef according to the parameters, calculate the lookup table {$IFDEF X86ASM} asm CMP ECX,8 JB @@8 PUSH EBX PUSH EDI PUSH ESI MOV [EAX].TCRCDef.Polynomial,EDX MOV [EAX].TCRCDef.Bits,ECX MOV EBX,InitVector MOV EDI,FinalVector MOV ESI,Inverse MOV [EAX].TCRCDef.CRC,EBX MOV [EAX].TCRCDef.InitVector,EBX MOV [EAX].TCRCDef.FinalVector,EDI MOV [EAX].TCRCDef.Inverse,ESI XOR EDI,EDI LEA EBX,[ECX - 8] SUB ECX,32 DEC EDI NEG ECX SHR EDI,CL MOV [EAX].TCRCDef.Shift,EBX MOV [EAX].TCRCDef.Mask,EDI TEST ESI,ESI JZ @@5 XOR EBX,EBX MOV ECX,[EAX].TCRCDef.Bits @@1: SHR EDX,1 ADC EBX,EBX DEC ECX JNZ @@1 NOP MOV ECX,255 NOP @@20: MOV EDX,ECX SHR EDX,1 JNC @@21 XOR EDX,EBX @@21: SHR EDX,1 JNC @@22 XOR EDX,EBX @@22: SHR EDX,1 JNC @@23 XOR EDX,EBX @@23: SHR EDX,1 JNC @@24 XOR EDX,EBX @@24: SHR EDX,1 JNC @@25 XOR EDX,EBX @@25: SHR EDX,1 JNC @@26 XOR EDX,EBX @@26: SHR EDX,1 JNC @@27 XOR EDX,EBX @@27: SHR EDX,1 JNC @@28 XOR EDX,EBX @@28: MOV [EAX + ECX * 4],EDX DEC ECX JNL @@20 JMP @@7 @@5: AND EDX,EDI ROL EDX,CL MOV EBX,255 // can be coded branchfree @@60: MOV ESI,EBX SHL ESI,25 JNC @@61 XOR ESI,EDX @@61: ADD ESI,ESI JNC @@62 XOR ESI,EDX @@62: ADD ESI,ESI JNC @@63 XOR ESI,EDX @@63: ADD ESI,ESI JNC @@64 XOR ESI,EDX @@64: ADD ESI,ESI JNC @@65 XOR ESI,EDX @@65: ADD ESI,ESI JNC @@66 XOR ESI,EDX @@66: ADD ESI,ESI JNC @@67 XOR ESI,EDX @@67: ADD ESI,ESI JNC @@68 XOR ESI,EDX @@68: ROR ESI,CL MOV [EAX + EBX * 4],ESI DEC EBX JNL @@60 @@7: POP ESI POP EDI POP EBX @@8: CMC SBB EAX,EAX NEG EAX end; {$ELSE !X86ASM} var Value, XorValue, OldValue: UInt32; Index: Integer; B: Boolean; One: Byte; begin if Bits >= 8 then begin CRCDef.Polynomial := Polynomial; CRCDef.Bits := Bits; CRCDef.CRC := InitVector; CRCDef.InitVector := InitVector; CRCDef.FinalVector := FinalVector; CRCDef.Inverse := Inverse; CRCDef.Shift := Bits - 8; Bits := -(Bits - 32); CRCDef.Mask := -1 shr Byte(Bits); if Inverse then begin Bits := CRCDef.Bits; XorValue := 0; repeat Inc(XorValue, XorValue + Ord(Polynomial and $1)); Polynomial := Polynomial shr 1; Dec(Bits); until Bits = 0; One := $1; for Index := 255 downto 0 do begin Value := Index; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; B := Boolean(Value and One); Value := Value shr 1; if B then Value := Value xor XorValue; CRCDef.Table[Index] := Value; end; end else begin XorValue := Polynomial and CRCDef.Mask; XorValue := (XorValue shl Byte(Bits)) or (XorValue shr (32 - Byte(Bits))); for Index := 255 downto 0 do begin B := Boolean(Index and $000000080); Value := Index shl 25; if B then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; OldValue := Value; Inc(Value, Value); if Value < OldValue then Value := Value xor XorValue; Value := (Value shr Byte(Bits)) or (Value shl (32 - Byte(Bits))); CRCDef.Table[Index] := Value; end; end; Result := True; end else Result := False; end; {$ENDIF !X86ASM} function CRCInit(var CRCDef: TCRCDef; CRCType: TCRCType): Boolean; begin Result := CRCSetup(CRCDef, PCRCTab(@CRCTab)[CRCType].Poly, PCRCTab(@CRCTab)[CRCType].Bits, PCRCTab(@CRCTab)[CRCType].Init, PCRCTab(@CRCTab)[CRCType].FInit, PCRCTab(@CRCTab)[CRCType].Inverse); end; function CRCCode(var CRCDef: TCRCDef; const Buffer; Size: UInt32): UInt32; // do the CRC computation {$IFDEF X86ASM} asm JECXZ @@5 TEST EDX,EDX JZ @@5 PUSH ESI PUSH EBX MOV ESI,EAX CMP [EAX].TCRCDef.Inverse,0 MOV EAX,[ESI].TCRCDef.CRC JZ @@2 XOR EBX,EBX @@1: MOV BL,[EDX] XOR BL,AL SHR EAX,8 INC EDX XOR EAX,[ESI + EBX * 4] DEC ECX JNZ @@1 JMP @@4 @@2: PUSH EDI MOV EBX,EAX MOV EDI,ECX MOV ECX,[ESI].TCRCDef.Shift MOV EBX,EAX @@3: SHR EBX,CL SHL EAX,8 XOR BL,[EDX] INC EDX MOVZX EBX,BL XOR EAX,[ESI + EBX * 4] DEC EDI MOV EBX,EAX JNZ @@3 POP EDI @@4: MOV [ESI].TCRCDef.CRC,EAX XOR EAX,[ESI].TCRCDef.FinalVector AND EAX,[ESI].TCRCDef.Mask POP EBX POP ESI RET @@5: MOV EAX,[EAX].TCRCDef.CRC end; {$ELSE !X86ASM} var P: PByte; Value: Byte; begin Result := CRCDef.CRC; P := @Buffer; if (Size <> 0) and (P <> nil) then begin if CRCDef.Inverse then begin repeat Value := P^ xor Byte(Result); Result := (Result shr 8) xor CRCDef.Table[Value]; Inc(P); Dec(Size); until Size = 0; end else begin Value := Byte(CRCDef.Shift); // move to local variable => cpu register repeat Result := (Result shl 8) xor CRCDef.Table[Byte(Result shr Value) xor P^]; Inc(P); Dec(Size); until Size = 0; end; CRCDef.CRC := Result; Result := (Result xor CRCDef.FinalVector) and CRCDef.Mask; end; end; {$ENDIF !X86ASM} function CRCCode(var CRCDef: TCRCDef; ReadMethod: TReadMethod; Size: UInt32 = $FFFFFFFF): UInt32; var Buffer: array[0..1023] of Char; Count: Int64; begin repeat if Size > SizeOf(Buffer) then Count := SizeOf(Buffer) else Count := Size; Count := ReadMethod(Buffer, Count); Result := CRCCode(CRCDef, Buffer, Count); Dec(Size, Count); until (Size = 0) or (Count = 0); end; function CRCDone(var CRCDef: TCRCDef): UInt32; // finalize CRCDef after a computation {$IFDEF X86ASM} asm MOV EDX,[EAX].TCRCDef.CRC MOV ECX,[EAX].TCRCDef.InitVector XOR EDX,[EAX].TCRCDef.FinalVector MOV [EAX].TCRCDef.CRC,ECX AND EDX,[EAX].TCRCDef.Mask MOV EAX,EDX end; {$ELSE !X86ASM} begin Result := CRCDef.CRC; CRCDef.CRC := CRCDef.InitVector; Result := (Result xor CRCDef.FinalVector) and CRCDef.Mask; end; {$ENDIF !X86ASM} function CRCCalc(CRCType: TCRCType; const Buffer; Size: UInt32): UInt32; // inplace calculation var CRC: TCRCDef; begin CRCInit(CRC, CRCType); Result := CRCCode(CRC, Buffer, Size); end; function CRCCalc(CRCType: TCRCType; ReadMethod: TReadMethod; Size: UInt32): UInt32; var CRC: TCRCDef; begin CRCInit(CRC, CRCType); Result := CRCCode(CRC, ReadMethod, Size); end; // predefined CRC16/CRC32CCITT, avoid slower lookuptable computation by use of precomputation var FCRC16: PCRCDef = nil; FCRC32: PCRCDef = nil; function CRC16Init: Pointer; begin // Replace GetMem by GetMemory due to C++ Builder compatibility // GetMem(FCRC16, SizeOf(TCRCDef)); FCRC16 := GetMemory(SizeOf(TCRCDef)); CRCInit(FCRC16^, CRC_16); Result := FCRC16; end; function CRC16(CRC: UInt16; const Buffer; Size: UInt32): UInt16; {$IFDEF X86ASM} asm JECXZ @@2 PUSH EDI PUSH ESI MOV EDI,ECX {$IFDEF PIC} MOV ESI,[EBX].FCRC16 {$ELSE !PIC} MOV ESI,FCRC16 {$ENDIF !PIC} XOR ECX,ECX TEST ESI,ESI JZ @@3 @@1: MOV CL,[EDX] XOR CL,AL SHR EAX,8 INC EDX XOR EAX,[ESI + ECX * 4] DEC EDI JNZ @@1 POP ESI POP EDI @@2: RET @@3: PUSH EAX PUSH EDX CALL CRC16Init MOV ESI,EAX XOR ECX,ECX POP EDX POP EAX JMP @@1 end; {$ELSE !X86ASM} var LCRC16: PCRCDef; P: PByte; CRC32: UInt32; Value: Byte; begin if Size <> 0 then begin LCRC16 := FCRC16; if LCRC16 = nil then LCRC16 := CRC16Init; CRC32 := CRC; P := @Buffer; repeat Value := P^ xor Byte(CRC32); CRC32 := (CRC32 shr 8) xor LCRC16.Table[Value]; Inc(P); Dec(Size); until Size = 0; Result := UInt16(CRC32); end else Result := CRC; end; {$ENDIF !X86ASM} function CRC32Init: Pointer; begin // Replaced for C++ Builder compatibility // GetMem(FCRC32, SizeOf(TCRCDef)); FCRC32 := GetMemory(SizeOf(TCRCDef)); CRCInit(FCRC32^, CRC_32CCITT); Result := FCRC32; end; function CRC32(CRC: UInt32; const Buffer; Size: UInt32): UInt32; {$IFDEF X86ASM} asm JECXZ @@2 PUSH EDI PUSH ESI NOT EAX // inverse Input CRC MOV EDI,ECX {$IFDEF PIC} MOV ESI,[EBX].FCRC32 {$ELSE !PIC} MOV ESI,FCRC32 {$ENDIF !PIC} XOR ECX,ECX TEST ESI,ESI JZ @@3 @@1: MOV CL,[EDX] XOR CL,AL SHR EAX,8 INC EDX XOR EAX,[ESI + ECX * 4] DEC EDI JNZ @@1 NOT EAX // inverse Output CRC POP ESI POP EDI @@2: RET @@3: PUSH EAX PUSH EDX CALL CRC32Init MOV ESI,EAX XOR ECX,ECX POP EDX POP EAX JMP @@1 end; {$ELSE !X86ASM} var LCRC32: PCRCDef; P: PByte; CRC32: UInt32; Value: Byte; begin if Size <> 0 then begin LCRC32 := FCRC32; if LCRC32 = nil then LCRC32 := CRC32Init; CRC32 := not CRC; // inverse Input CRC P := @Buffer; repeat Value := P^ xor Byte(CRC32); CRC32 := (CRC32 shr 8) xor LCRC32.Table[Value]; Inc(P); Dec(Size); until Size = 0; Result := not CRC32; // inverse Output CRC end else Result := CRC; end; {$ENDIF !X86ASM} procedure CRCInitThreadSafe; begin CRC16Init; CRC32Init; end; {$IFDEF RESTORE_RANGECHECKS}{$R+}{$ENDIF} {$IFDEF RESTORE_OVERFLOWCHECKS}{$Q+}{$ENDIF} initialization CRCInitThreadSafe; finalization if FCRC16 <> nil then FreeMem(FCRC16); if FCRC32 <> nil then FreeMem(FCRC32); end.