CRC32算法详细推导(3)

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

CRC32算法详细推导(3)

郁闷的位逆转

看起来我们已经得到 CRC-32 算法的最终形式了,可是、可是在实际的应用中,数据传输时是低位先行的;对于一个字节 Byte 来讲,传输将是按照 b1,b2,...,b8 的顺序。而我们上面的算法是按照高位在前的约定,不管是 reg还是 G(x) , g32,g31,...,g1 ; b8,b7,...,b1 ; r32,r31,...,r1 。

先来看看前面从 bit 转换到 Byte 一节中 for 循环的逻辑:

[cpp]  view plain copy
  1.               sum_poly = reg&0xFF000000;  
  2. for(int j = 0; j < 8; j++)  
  3. {  
  4.     int hi = sum_poly&0x80000000; // 测试reg最高位  
  5.     sum_poly <<= 1;  
  6.     if(hi) sum_poly = sum_poly^POLY;  
  7. }  
  8. // 计算步骤2  
  9. reg = (reg<<8)|p[i];  
  10. reg = reg ^ sum_poly;  

在这里的计算中, p[i] 是按照 p8,p7,...,p1 的顺序;如果 p[i] 在这里变成了 p1,p2,...,p8 的顺序;那么 reg 也应该是 r1,r2,...,r32 的顺序,同样 G(x) 和 sum_poly 也要逆转顺序。转换后的 G(x) = POLY = 0xEDB88320 。

于是取 reg 的最高位的 sum_poly 的初值就从 sum_poly = reg & 0xFF000000 变成了 sum_poly = reg & 0xFF,测试 reg 的最高位就从 sum_poly & 0x80000000 变成了 sum_poly&0x01 ;

移出最高位也就从 sum_poly<<=1 变成了 sum_poly>>=1 ;于是上面的代码就变成了如下的形式:

[cpp]  view plain copy
  1.               sum_poly = reg&0xFF;  
  2. for(int j = 0; j < 8; j++)  
  3. {  
  4.     int hi = sum_poly&0x01; // 测试reg最高位  
  5.     sum_poly >>= 1;  
  6.     if(hi) sum_poly = sum_poly^POLY;  
  7. }  
  8. // 计算步骤2  
  9. reg = (reg<<8)|p[i];  
  10. reg = reg ^ sum_poly;  

为了清晰起见,给出完整的代码:

[cpp]  view plain copy
  1. // 以4 byte数据为例  
  2. #define POLY 0xEDB88320L // CRC32生成多项式  
  3. unsigned int CRC32_2(unsigned int data)  
  4. {  
  5.     unsigned char p[8];  
  6.     memset(p, 0, sizeof(p));  
  7.     memcpy(p, &data, 4);  
  8.     unsigned int reg = 0, sum_poly = 0;  
  9.     for(int i = 0; i < 8; i++)  
  10.     {  
  11.         // 计算步骤1  
  12.         sum_poly = reg&0xFF;  
  13.         for(int j = 0; j < 8; j++)  
  14.         {  
  15.             int hi = sum_poly&0x01; // 测试reg最高位  
  16.             sum_poly >>= 1;  
  17.             if(hi) sum_poly = sum_poly^POLY;  
  18.         }  
  19.         // 计算步骤2  
  20.         reg = (reg<<8)|p[i];  
  21.         reg = reg ^ sum_poly;  
  22.     }  
  23.     return reg;  
  24. }  

依旧像上面的思路,把计算 sum_poly 的代码段提取出来,生成 256 个元素的 CRC 校验表,再修改追加 0 的逻辑,最终的代码版本就完成了,为了对比;后面给出了字节序逆转前的完整代码段。

[cpp]  view plain copy
  1. // 字节逆转后的CRC32算法,字节序为b1,b2,…,b8  
  2. #define POLY 0xEDB88320L // CRC32生成多项式  
  3. static unsigned int crc_table[256];  
  4. unsigned int get_sum_poly(unsigned char data)  
  5. {  
  6.     unsigned int sum_poly = data;  
  7.     for(int j = 0; j < 8; j++)  
  8.     {  
  9.         int hi = sum_poly&0x01; // 取得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. unsigned int CRC32_4(unsigned char* data, int len)  
  23. {  
  24.     unsigned int reg = 0; // 0xFFFFFFFF,见后面解释  
  25.     for(int i = 0; i < len; i++)  
  26.     {  
  27.         reg = (reg<<8) ^ crc_table[(reg&0xFF) ^ data[i]];  
  28.         return reg;  
  29.     }  
  30. }  
  31. // 最终生成的校验表将是:  
  32. // {0x00000000,  0x77073096,  0xEE0E612C,  0x990951BA,  
  33. //  0x076DC419,  0x706AF48F,  0xE963A535,  0x9E6495A3,  
  34. // … …}   
  35. // 字节逆转前的CRC32算法,字节序为b8,b7,…,b1  
  36. #define POLY 0x04C11DB7L // CRC32生成多项式  
  37. static unsigned int crc_table[256];  
  38. unsigned int get_sum_poly(unsigned char data)  
  39. {  
  40.     unsigned int sum_poly = data;  
  41.     sum_poly <<= 24;  
  42.     for(int j = 0; j < 8; j++)  
  43.     {  
  44.         int hi = sum_poly&0x80000000; // 取得reg的最高位  
  45.         sum_poly <<= 1;  
  46.         if(hi) sum_poly = sum_poly^POLY;  
  47.     }  
  48.     return sum_poly;  
  49. }  
  50. void create_crc_table()  
  51. {  
  52.     for(int i = 0; i < 256; i++)  
  53.     {  
  54.         crc_table[i] = get_sum_poly(i&0xFF);  
  55.     }  
  56. }   
  57. unsigned int CRC32_4(unsigned char* data, int len)  
  58. {  
  59.     unsigned int reg = 0;// 0xFFFFFFFF,见后面解释  
  60.     for(int i = 0; i < len; i++)  
  61.     {  
  62.         reg = (reg<<8) ^ crc_table[(reg>>24)&0xFF ^ data[i]];  
  63.         return reg;  
  64.     }  
  65. }  

长征结束了

到这里长征终于结束了, 事实上,还有最后的一小步,那就是 reg 初始值的问题,上面的算法中 reg 初始值为 0 。在一些传输协议中,发送端并不指出消息长度,而是采用结束标志,考虑下面的这几种可能的差错:

1 )在消息之前,增加 1 个或多个 0 字节;

2) 在消息 ( 包括校验码 ) 之后,增加 1 个或多个 0 字节;

显然,这几种差错都检测不出来,其原因就是如果 reg=0 ,处理 0 消息字节 ( 或位 ) , reg 的值保持不变。解决这种问题也很简单,只要使 reg 的初始值非 0 即可,一般取 0Xffffffff ,就像你在很多 CRC32 实现中发现的那样。

到这里终于可以松一口气了, CRC32 并不是像想象的那样容易的算法啊!事实上还真不容易!这就叫做“简单的前面是优雅,背后是复杂”!


你可能感兴趣的:(CRC32算法详细推导(3))