Delphi 汇编学习(十二)--- CRC32 校验

23 篇文章 1 订阅
15 篇文章 2 订阅

学习 SIMD 指令时,发现一条 crc32 指令,一看就知道,肯定是用来进行 CRC 校验的。
在网上搜索了一番,BAIDU 不出什么有用的信息。很多代码,连基本的 CRC32 校验代码都是错的。
GOOGLE 搜索了一些,都是 C++ 的。但 C++ 都倾向于写算法,没有什么具体的示例。也不知道对不对。
看来只好自己写来验证了。我不敢说我写的肯定正确,但我会经过慎重测试,确认没有问题后,才公布。

CRC32 校验算法有很多种:CRC32 IEEE 802.3、CRC32 Castagnoli、CRC32 Koopman,等等。
CRC32 IEEE 802.3,下面简称 CRC32I。
CRC32 Castagnoli, 下面简称 CRC32C。
CRC32 Koopman,   下面简称 CRC32K。
这三种校验算法,它们的算法都是相同的,只是多项式不同。所以在进行表优化编程时,导致常量表不同。值当然也是不同的。

这里只介绍 CRC32I 和 CRC32C 这两种检验算法。因为它们比较常用。
Windows 平台下,字符串校验、文件校验,用的基本上都是第一种:CRC32I。

通过:Delphi 汇编学习(一)--- 图像灰值化,这篇文章,我们知道,内存定位速度(表优化)远低于 CPU 寄存器运算速度。
也就是说表优化速度有限,不如 CPU 指令来的快。这或许也是 crc32 指令由来的原因吧。

需要特别注意的是:
  SSE4.2 指令中的 crc32 指令,是针对 CRC32 Castagnoli 校验算法进行优化的。
  你在互联网上经常看到 crc32c 这个名称,说的就是这个校验算法。不是 CRC32I。这个一定要注意。
  SSE4.2 中的 crc32 指令检验出来的值,和 CRC32I 校验的值是不一样的。因为 CRC32I、CRC32C,多项式不一样(常量表不同)。
  SSE4.2 中的 crc32 指令校验出来的值,和 CRC32C 表优化算法校验出来的值应该是一样的。也必须是一样的。

首先来定义这两个算法的常量表:

const
  c_CRC32I_Table: array [0 .. 255] of DWORD = (                                                                                                                                     //
    $00000000, $77073096, $EE0E612C, $990951BA, $076DC419, $706AF48F, $E963A535, $9E6495A3, $0EDB8832, $79DCB8A4, $E0D5E91E, $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91, //
    $1DB71064, $6AB020F2, $F3B97148, $84BE41DE, $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856, $646BA8C0, $FD62F97A, $8A65C9EC, $14015C4F, $63066CD9, $FA0F3D63, $8D080DF5, //
    $3B6E20C8, $4C69105E, $D56041E4, $A2677172, $3C03E4D1, $4B04D447, $D20D85FD, $A50AB56B, $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, $45DF5C75, $DCD60DCF, $ABD13D59, //
    $26D930AC, $51DE003A, $C8D75180, $BFD06116, $21B4F4B5, $56B3C423, $CFBA9599, $B8BDA50F, $2802B89E, $5F058808, $C60CD9B2, $B10BE924, $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, //
    $76DC4190, $01DB7106, $98D220BC, $EFD5102A, $71B18589, $06B6B51F, $9FBFE4A5, $E8B8D433, $7807C9A2, $0F00F934, $9609A88E, $E10E9818, $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, //
    $6B6B51F4, $1C6C6162, $856530D8, $F262004E, $6C0695ED, $1B01A57B, $8208F4C1, $F50FC457, $65B0D9C6, $12B7E950, $8BBEB8EA, $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3, $FBD44C65, //
    $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, $4ADFA541, $3DD895D7, $A4D1C46D, $D3D6F4FB, $4369E96A, $346ED9FC, $AD678846, $DA60B8D0, $44042D73, $33031DE5, $AA0A4C5F, $DD0D7CC9, //
    $5005713C, $270241AA, $BE0B1010, $C90C2086, $5768B525, $206F85B3, $B966D409, $CE61E49F, $5EDEF90E, $29D9C998, $B0D09822, $C7D7A8B4, $59B33D17, $2EB40D81, $B7BD5C3B, $C0BA6CAD, //
    $EDB88320, $9ABFB3B6, $03B6E20C, $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, $73DC1683, $E3630B12, $94643B84, $0D6D6A3E, $7A6A5AA8, $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1, //
    $F00F9344, $8708A3D2, $1E01F268, $6906C2FE, $F762575D, $806567CB, $196C3671, $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC, $F9B9DF6F, $8EBEEFF9, $17B7BE43, $60B08ED5, //
    $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1, $A6BC5767, $3FB506DD, $48B2364B, $D80D2BDA, $AF0A1B4C, $36034AF6, $41047A60, $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79, //
    $CB61B38C, $BC66831A, $256FD2A0, $5268E236, $CC0C7795, $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, $B2BD0B28, $2BB45A92, $5CB36A04, $C2D7FFA7, $B5D0CF31, $2CD99E8B, $5BDEAE1D, //
    $9B64C2B0, $EC63F226, $756AA39C, $026D930A, $9C0906A9, $EB0E363F, $72076785, $05005713, $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, $E5D5BE0D, $7CDCEFB7, $0BDBDF21, //
    $86D3D2D4, $F1D4E242, $68DDB3F8, $1FDA836E, $81BE16CD, $F6B9265B, $6FB077E1, $18B74777, $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, $8F659EFF, $F862AE69, $616BFFD3, $166CCF45, //
    $A00AE278, $D70DD2EE, $4E048354, $3903B3C2, $A7672661, $D06016F7, $4969474D, $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66, $37D83BF0, $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, //
    $BDBDF21C, $CABAC28A, $53B39330, $24B4A3A6, $BAD03605, $CDD70693, $54DE5729, $23D967BF, $B3667A2E, $C4614AB8, $5D681B02, $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D  //
    );

  c_CRC32C_Table: array [0 .. 255] of DWORD = (                                                                                                                                     //
    $00000000, $F26B8303, $E13B70F7, $1350F3F4, $C79A971F, $35F1141C, $26A1E7E8, $D4CA64EB, $8AD958CF, $78B2DBCC, $6BE22838, $9989AB3B, $4D43CFD0, $BF284CD3, $AC78BF27, $5E133C24, //
    $105EC76F, $E235446C, $F165B798, $030E349B, $D7C45070, $25AFD373, $36FF2087, $C494A384, $9A879FA0, $68EC1CA3, $7BBCEF57, $89D76C54, $5D1D08BF, $AF768BBC, $BC267848, $4E4DFB4B, //
    $20BD8EDE, $D2D60DDD, $C186FE29, $33ED7D2A, $E72719C1, $154C9AC2, $061C6936, $F477EA35, $AA64D611, $580F5512, $4B5FA6E6, $B93425E5, $6DFE410E, $9F95C20D, $8CC531F9, $7EAEB2FA, //
    $30E349B1, $C288CAB2, $D1D83946, $23B3BA45, $F779DEAE, $05125DAD, $1642AE59, $E4292D5A, $BA3A117E, $4851927D, $5B016189, $A96AE28A, $7DA08661, $8FCB0562, $9C9BF696, $6EF07595, //
    $417B1DBC, $B3109EBF, $A0406D4B, $522BEE48, $86E18AA3, $748A09A0, $67DAFA54, $95B17957, $CBA24573, $39C9C670, $2A993584, $D8F2B687, $0C38D26C, $FE53516F, $ED03A29B, $1F682198, //
    $5125DAD3, $A34E59D0, $B01EAA24, $42752927, $96BF4DCC, $64D4CECF, $77843D3B, $85EFBE38, $DBFC821C, $2997011F, $3AC7F2EB, $C8AC71E8, $1C661503, $EE0D9600, $FD5D65F4, $0F36E6F7, //
    $61C69362, $93AD1061, $80FDE395, $72966096, $A65C047D, $5437877E, $4767748A, $B50CF789, $EB1FCBAD, $197448AE, $0A24BB5A, $F84F3859, $2C855CB2, $DEEEDFB1, $CDBE2C45, $3FD5AF46, //
    $7198540D, $83F3D70E, $90A324FA, $62C8A7F9, $B602C312, $44694011, $5739B3E5, $A55230E6, $FB410CC2, $092A8FC1, $1A7A7C35, $E811FF36, $3CDB9BDD, $CEB018DE, $DDE0EB2A, $2F8B6829, //
    $82F63B78, $709DB87B, $63CD4B8F, $91A6C88C, $456CAC67, $B7072F64, $A457DC90, $563C5F93, $082F63B7, $FA44E0B4, $E9141340, $1B7F9043, $CFB5F4A8, $3DDE77AB, $2E8E845F, $DCE5075C, //
    $92A8FC17, $60C37F14, $73938CE0, $81F80FE3, $55326B08, $A759E80B, $B4091BFF, $466298FC, $1871A4D8, $EA1A27DB, $F94AD42F, $0B21572C, $DFEB33C7, $2D80B0C4, $3ED04330, $CCBBC033, //
    $A24BB5A6, $502036A5, $4370C551, $B11B4652, $65D122B9, $97BAA1BA, $84EA524E, $7681D14D, $2892ED69, $DAF96E6A, $C9A99D9E, $3BC21E9D, $EF087A76, $1D63F975, $0E330A81, $FC588982, //
    $B21572C9, $407EF1CA, $532E023E, $A145813D, $758FE5D6, $87E466D5, $94B49521, $66DF1622, $38CC2A06, $CAA7A905, $D9F75AF1, $2B9CD9F2, $FF56BD19, $0D3D3E1A, $1E6DCDEE, $EC064EED, //
    $C38D26C4, $31E6A5C7, $22B65633, $D0DDD530, $0417B1DB, $F67C32D8, $E52CC12C, $1747422F, $49547E0B, $BB3FFD08, $A86F0EFC, $5A048DFF, $8ECEE914, $7CA56A17, $6FF599E3, $9D9E1AE0, //
    $D3D3E1AB, $21B862A8, $32E8915C, $C083125F, $144976B4, $E622F5B7, $F5720643, $07198540, $590AB964, $AB613A67, $B831C993, $4A5A4A90, $9E902E7B, $6CFBAD78, $7FAB5E8C, $8DC0DD8F, //
    $E330A81A, $115B2B19, $020BD8ED, $F0605BEE, $24AA3F05, $D6C1BC06, $C5914FF2, $37FACCF1, $69E9F0D5, $9B8273D6, $88D28022, $7AB90321, $AE7367CA, $5C18E4C9, $4F48173D, $BD23943E, //
    $F36E6F75, $0105EC76, $12551F82, $E03E9C81, $34F4F86A, $C69F7B69, $D5CF889D, $27A40B9E, $79B737BA, $8BDCB4B9, $988C474D, $6AE7C44E, $BE2DA0A5, $4C4623A6, $5F16D052, $AD7D5351  //
    );

Delphi 标准的 CRC32I 函数:

{ CRC32 IEEE 802.3 Table }
function CRC32I_Table(Buffer: PByte; len: Integer): DWORD;
var
  I: Integer;
begin
  Result := $FFFFFFFF;
  for I  := 0 to len - 1 do
  begin
    Result := ((Result shr 8) and $00FFFFFF) xor c_CRC32I_Table[(Result xor Buffer^) and $FF];
    Inc(Buffer);
  end;
  Result := Result xor $FFFFFFFF;
end;

Delphi 标准的 CRC32C 函数(和 CRC32I 算法相同,常量表不同):

{ CRC32 Castagnoli Table }
function CRC32C_Table(Buffer: PByte; len: Integer): DWORD;
var
  I: Integer;
begin
  Result := $FFFFFFFF;
  for I  := 0 to len - 1 do
  begin
    Result := ((Result shr 8) and $00FFFFFF) xor c_CRC32C_Table[(Result xor Buffer^) and $FF];
    Inc(Buffer);
  end;
  Result := Result xor $FFFFFFFF;
end;

Delphi 标准的 CRC32C SSE42 函数:

function CRC32C_SSE42(Buffer: PByte; len: Integer): DWORD;
begin
  Result := _crc32c_append_hw(0, Buffer, len);
end;

_crc32c_append_hw 函数来自于 GitHub - robertvazan/crc32c-hw: Hardware-accelerated implementation of CRC-32C (Castagnoli).,经测试,也只有它是正确的。
得出的校验值和 CRC32C_Table 函数是一样的。
从 GITHUB 上扒下来很多代码,使用 crc32 指令的代码,都运算不出正确的结果。
我编译了一下,拿过来就用了。有时间时在用汇编来写吧。重复劳动没有什么意义。当然学习写汇编的意义还是有的。

这上面三个函数你可以验证正确性。我测试下来都是没问题的。
Delphi 中 IdHashCRC.pas、System.zlib.pas 中都有 CRC32I 的校验算法,你可以进行验证。
( 其实 CRC32I_Table 函数就是对 IdHashCRC.pas 的复制和改写)

缓冲区校验代码结束了。接下来肯定是文件的校验。小文件的校验肯定没什么问题,关键是大文件的校验。2G大小以上大文件的校验。
首先让我们来看看上面的这三个函数,它们的函数定义都是一样的。所以我们可以定义一个通用类型:

type
  TCRC32Func = function(Buffer: PByte; len: Integer): DWORD;

这样我们就可以将上面的三个函数通过参数进行传递。
下面是针对文件校验的三个函数:

{ 文件 CRC32 CRC32I_Table }
function CRC32I_Table_File(const strFileName: string): DWORD;
begin
  Result := CommonCRC32(strFileName, CRC32I_Table);
end;

{ 文件 CRC32 CRC32C_Table }
function CRC32C_Table_File(const strFileName: string): DWORD;
begin
  Result := CommonCRC32(strFileName, CRC32C_Table);
end;

{ 文件 CRC32 CRC32C_SSE42 }
function CRC32C_SSE42_File(const strFileName: string): DWORD;
begin
  Result := CommonCRC32(strFileName, CRC32C_SSE42);
end;

封装的 CommonCRC32 函数如下(当然是支持校验大文件的):

function CommonCRC32(const strFileName: string; CRC32Func: TCRC32Func): DWORD;
var
  iFileHandle: THandle;
  iFileLength: UInt64;
  MapHandle  : THandle;
  ViewPointer: Pointer;
  FileInfo   : TByHandleFileInformation;
begin
  Result := 0;

  { 文件是否存在 }
  if not FileExists(strFileName) then
    Exit;

  { 文件是否能正确打开 }
  iFileHandle := FileOpen(strFileName, fmOpenRead);
  if iFileHandle = INVALID_HANDLE_VALUE then
    Exit;

  try
    { 获取文件大小。GetFileSize、FileSeek 等等函数,针对2G以上大文件都是无效的。必须使用此函数 }
    if not GetFileInformationByHandle(iFileHandle, FileInfo) then
      Exit;

    { 文件大小不能为 0 }
    iFileLength := UInt64(FileInfo.nFileSizeHigh) shl 32 + FileInfo.nFileSizeLow;
    if iFileLength = 0 then
      Exit;

    { 创建文件映射。必须这么写,否则映射会失败 }
    MapHandle := CreateFileMapping(iFileHandle, nil, PAGE_READONLY, 0, iFileLength, nil);
    if MapHandle = INVALID_HANDLE_VALUE then
      Exit;

    try
     { 获取文件映射指针 }
      ViewPointer := MapViewOfFile(MapHandle, FILE_MAP_READ, 0, 0, 0);
      if ViewPointer = nil then
        Exit;

      try
       { 进行 CRC32 校验 }
        Result := CRC32Func(ViewPointer, iFileLength);
      finally
        UnmapViewOfFile(ViewPointer);
      end;
    finally
      CloseHandle(MapHandle);
    end;
  finally
    FileClose(iFileHandle);
  end;
end;

代码结束。你可以写调用代码测试文件校验了。
测试下来,使用了 SSE4.2 指令 crc32 的速度是表优化速度的10倍之多。针对大文件最好不过了。

测试环境:WIN10X64 + Delphi10.4.2
源码地址:GitHub - dbyoung720/CRC32: Buffer, File : CRC32I, CRC32C, CRC32CSSE
qq交流群:101611228

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以使用以下代码来计算 Delphi 中的 CRC16 CCITT-FALSE 校验值: ```Delphi function CalcCRC16(const Data: array of Byte; Len: Integer): Word; const Crc16Table: array[0..255] of Word = ( $0000, $1021, $2042, $3063, $4084, $50A5, $60C6, $70E7, $8108, $9129, $A14A, $B16B, $C18C, $D1AD, $E1CE, $F1EF, $1231, $0210, $3273, $2252, $52B5, $4294, $72F7, $62D6, $9339, $8318, $B37B, $A35A, $D3BD, $C39C, $F3FF, $E3DE, $2462, $3443, $0420, $1401, $64E6, $74C7, $44A4, $5485, $A56A, $B54B, $8528, $9509, $E5EE, $F5CF, $C5AC, $D58D, $3653, $2672, $1611, $0630, $76D7, $66F6, $5695, $46B4, $B75B, $A77A, $9719, $8738, $F7DF, $E7FE, $D79D, $C7BC, $48C4, $58E5, $6886, $78A7, $0840, $1861, $2802, $3823, $C9CC, $D9ED, $E98E, $F9AF, $8948, $9969, $A90A, $B92B, $5AF5, $4AD4, $7AB7, $6A96, $1A71, $0A50, $3A33, $2A12, $DBFD, $CBDC, $FBBF, $EB9E, $9B79, $8B58, $BB3B, $AB1A, $6CA6, $7C87, $4CE4, $5CC5, $2C22, $3C03, $0C60, $1C41, $EDAE, $FD8F, $CDEC, $DDCD, $AD2A, $BD0B, $8D68, $9D49, $7E97, $6EB6, $5ED5, $4EF4, $3E13, $2E32, $1E51, $0E70, $FF9F, $EFBE, $DFDD, $CFFC, $BF1B, $AF3A, $9F59, $8F78, $9188, $81A9, $B1CA, $A1EB, $D10C, $C12D, $F14E, $E16F, $1080, $00A1, $30C2, $20E3, $5004, $4025, $7046, $6067, $83B9, $9398, $A3FB, $B3DA, $C33D, $D31C, $E37F, $F35E, $02B1, $1290, $22F3, $32D2, $4235, $5214, $6277, $7256, $B5EA, $A5CB, $95A8, $8589, $F56E, $E54F, $D52C, $C50D, $34E2, $24C3, $14A0, $0481, $7466, $6447, $5424, $4405, $A7DB, $B7FA, $8799, $97B8, $E75F, $F77E, $C71D, $D73C, $26D3, $36F2, $0691, $16B0, $6657, $7676, $4615, $5634, $D94C, $C96D, $F90E, $E92F, $99C8, $89E9, $B98A, $A9AB, $5844, $4865, $7806, $6827, $18C0, $08E1, $3882, $28A3, $CB7D, $DB5C, $EB3F, $FB1E, $8BF9, $9BD8, $ABBB, $BB9A, $4A75, $5A54, $6A37, $7A16, $0AF1, $1AD0, $2AB3, $3A92, $FD2E, $ED0F, $DD6C, $CD4D, $BDAA, $AD8B, $9DE8, $8DC9, $7C26, $6C07, $5C64, $4C45, $3CA2, $2C83, $1CE0, $0CC1, $EF1F, $FF3E, $CF5D, $DF7C, $AF9B, $BFBA, $8FD9, $9FF8, $6E17, $7E36, $4E55, $5E74, $2E93, $3EB2, $0ED1, $1EF0); var I: Integer; Crc: Word; begin Crc := $FFFF; for I := 0 to Len - 1 do begin Crc := (Crc shr 8) xor Crc16Table[(Crc xor Data[I]) and $FF]; end; Result := Crc; end; ``` 你可以将需要计算 CRC 校验值的字节数组和字节数组的长度作为参数传入该函数,函数会返回计算出的 CRC16 校验值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值