现在的位置: 首页 > 综合 > 正文

CRC32算法详细推导(2)

2018年04月02日 ⁄ 综合 ⁄ 共 3566字 ⁄ 字号 评论关闭

From: http://blog.csdn.net/sparkliang/article/details/5671977

CRC算法详解(2)

初见 Table-Driven

 

变换到上面的方法后,我们离 table-driven 的方法只有一步之遥了,我们知道一个字节能表示的正整数范围是 0~255,步骤 1 中的计算就是针对 reg 的高 Byte 位进行的,于是可以被提取出来,预先计算并存储到一个有 256 项的表中,于是下面的算法就出炉了,这个和上面的算法本质上并没有什么区别。

  1. #define POLY 0x04C11DB7L // CRC32生成多项式  
  2. static unsigned int crc_table[256];  
  3. unsigned int get_sum_poly(unsigned char data)  
  4. {  
  5.     unsigned int sum_poly = data;  
  6.     sum_poly <<= 24;  
  7.     for(int j = 0; j < 8; j++)  
  8.     {  
  9.         int hi = sum_poly&0x80000000; // 取得reg的最高位  
  10.         sum_poly <<= 1;  
  11.         if(hi) sum_poly = sum_poly^POLY;  
  12.     }  
  13.     return sum_poly;  
  14. }  
  15. void create_crc_table()  
  16. {  
  17.     for(int i = 0; i < 256; i++)  
  18.     {  
  19.         crc_table[i] = get_sum_poly(i&0xFF);  
  20.     }  
  21. }  
  22. // 以byte数据为例  
  23. unsigned int CRC32_3(unsigned int data)  
  24. {  
  25.     unsigned char p[8];  
  26.     memset(p, 0, sizeof(p));  
  27.     memcpy(p, &data, 4);  
  28.     unsigned int reg = 0, sum_poly = 0;  
  29.     for(int i = 0; i < 8; i++)  
  30.     {  
  31.         // 计算步骤1  
  32.         sum_poly = crc_table[(reg>>24)&0xFF];  
  33.         // 计算步骤2  
  34.         reg = (reg<<8)|p[i];  
  35.         reg = reg ^ sum_poly;  
  36.     }  
  37.     return reg;  
  38. }  

更进一步

 

上面的这个算法已经是一个Table-Driven的CRC-32算法了,但是实际上我们看到的CRC校验代码都是如下的形式:

<div><div><div style="color:silver;"><strong>[cpp]</strong> <a target=_blank href="http://blog.csdn.net/sparkliang/article/details/5671977#" title="view plain" style="color:rgb(160, 160, 160);">view plain</a><a target=_blank href="http://blog.csdn.net/sparkliang/article/details/5671977#" title="copy" style="color:rgb(160, 160, 160);">copy</a></div></div><ol start="1" style="color:rgb(92, 92, 92);"><li style="color:inherit;"><span style="color:black;">r=0;  </span></li><li><span style="color:black;"><span style="color:rgb(0, 102, 153);">while</span>(len--)  </span></li><li style="color:inherit;"><span style="color:black;">     r = (r<<8) ^ t[(r >> 24) ^ *p++];  </span></li></ol></div> 

下面我们将看看是做了什么转化而做到这一点的。

 

 

首先上述 CRC 算法中,我们需要为原始数据追加 r/8Byte 个 0 , CRC-32 就是 4Byte 。或者我们可以再计算原始数据之后,把 0 放在后面单独计算,像这样:

  1. // 先计算原始数据  
  2. for(int i = 0; i < len; i++)  
  3. {  
  4.         sum_poly = crc_table[(reg>>24)&0xFF];  
  5.         reg = (reg<<8)|p[i];  
  6.         reg = reg ^ sum_poly;  
  7. }  
  8. // 再计算追加的4Byte 0  
  9. for(int i = 0; i < 4; i++)  
  10. {  
  11.         reg = (reg<<8) ^ crc_table[(reg>>24)&0xFF];  
  12. }  

这看起来已经足够好了,而事实上我们可以继续向下进行以免去为了附加的 0 而进行计算。在上面算法中,最后的 4 次循环是为了将输入数据的最后 r/8 位都移出 reg ,因为 0 对 reg 的值并没有丝毫影响。

对于 CRC-32 ,对于任何输入数据 Dn...D8…D5D4…D1 ,第一个 for 循环将 Dn…D8…D5 都依次移入,执行XOR 运算再移出 reg ;并将 D4…D1 都移入了 reg ,但是并未移出;因此最后的 4 次循环是为了将 D4…D1 都移出 reg 。

Di 与 Ri 执行 XOR 运算后值将会更新,设更新后的值表示为 Di’ ,不论执行了多少次 XOR 运算。

如果 reg 初始值是 0 ,那么第一个 for 循环中开始的 4 次循环干的事情就是,把 Dn…Dn-3 移入到 reg 中(与0 做 XOR 结果不变),执行 4 次后 reg 的值就是 Dn.Dn-1.Dn-2.Dn-3 ;

第 5 次循环的结果就是: reg = crc_table[Dn] ^ Dn-1.Dn-2.Dn-3.Dn-4 ;

第 6 次循环的结果就是: reg = crc_table[Dn-1’] ^ Dn-2’.Dn-3’.Dn-4 ; Dn 移出 reg 。

因此上面的计算可以分为 3 个阶段:

1 )前 4 次循环,将 Dn.Dn-1.Dn-2.Dn-3 装入 reg ;

2 )中间的 n-4 次循环,依次将 Di 移入 reg ,在随后的 4 次循环中,依次计算 Di+4 , Di+3 , Di+2 和 Di+1对 Di 的影响;最后移出 reg ;  

3 )最后的 4 次循环,实际上是为了计算 D4 , D3 , D2 和 D1 都能执行第 2 步的过程;

具体考察 Di :

1 ) Di 移入到 reg 中, R1=Di ,接着与 crc_table[R4] 执行 XOR 运算;

2 )循环 4 次后, Di 成为 reg 的最高位 R4 ,并且因为受到了 Dn…Di+1 的影响而更新为 Di’ ;

上面的运算步骤如下面所示,其中 F 是对应得 crc_table[R] 的值。

可以清晰的看到,最后 reg 的高 Byte 是 Di 和 F 之间一次次 XOR 运算的结果。依然根据 XOR 运算的结合律,我们可以分两步走:

1)  先执行 F 之间的 XOR 运算,设结果为 FF ,它就是 reg 的首字节;

2)  然后再直接将 Di 和 FF 进行 XOR 运算,并根据结果查 CRC 表;

3)  计算出 XOR 运算后, Di…Di-3 已经移入 reg ;因此再将查表结果和 (reg<<8) 执行 XOR 运算即可;

这就是方法 2 ,于是我们的 table-driven 的 CRC-32 校验算法就可以写成如下的方式了:

  1. reg = 0;  
  2. for(int i = 0; i < len; i++)  
  3. {  
  4.     reg = (reg<<8) ^ crc_table[(reg>>24)&0xFF ^ p[i]];  
  5. }  

抱歉!评论已关闭.