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 循环的逻辑:
- sum_poly = reg&0xFF000000;
- for(int j = 0; j < 8; j++)
- {
- int hi = sum_poly&0x80000000; // 测试reg最高位
- sum_poly <<= 1;
- if(hi) sum_poly = sum_poly^POLY;
- }
- // 计算步骤2
- reg = (reg<<8)|p[i];
- 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 ;于是上面的代码就变成了如下的形式:
- sum_poly = reg&0xFF;
- for(int j = 0; j < 8; j++)
- {
- int hi = sum_poly&0x01; // 测试reg最高位
- sum_poly >>= 1;
- if(hi) sum_poly = sum_poly^POLY;
- }
- // 计算步骤2
- reg = (reg<<8)|p[i];
- reg = reg ^ sum_poly;
为了清晰起见,给出完整的代码:
- // 以4 byte数据为例
- #define POLY 0xEDB88320L // CRC32生成多项式
- unsigned int CRC32_2(unsigned int data)
- {
- unsigned char p[8];
- memset(p, 0, sizeof(p));
- memcpy(p, &data, 4);
- unsigned int reg = 0, sum_poly = 0;
- for(int i = 0; i < 8; i++)
- {
- // 计算步骤1
- sum_poly = reg&0xFF;
- for(int j = 0; j < 8; j++)
- {
- int hi = sum_poly&0x01; // 测试reg最高位
- sum_poly >>= 1;
- if(hi) sum_poly = sum_poly^POLY;
- }
- // 计算步骤2
- reg = (reg<<8)|p[i];
- reg = reg ^ sum_poly;
- }
- return reg;
- }
依旧像上面的思路,把计算 sum_poly 的代码段提取出来,生成 256 个元素的 CRC 校验表,再修改追加 0 的逻辑,最终的代码版本就完成了,为了对比;后面给出了字节序逆转前的完整代码段。
- // 字节逆转后的CRC32算法,字节序为b1,b2,…,b8
- #define POLY 0xEDB88320L // CRC32生成多项式
- static unsigned int crc_table[256];
- unsigned int get_sum_poly(unsigned char data)
- {
- unsigned int sum_poly = data;
- for(int j = 0; j < 8; j++)
- {
- int hi = sum_poly&0x01; // 取得reg的最高位
- sum_poly >>= 1;
- if(hi) sum_poly = sum_poly^POLY;
- }
- return sum_poly;
- }
- void create_crc_table()
- {
- for(int i = 0; i < 256; i++)
- {
- crc_table[i] = get_sum_poly(i&0xFF);
- }
- }
- unsigned int CRC32_4(unsigned char* data, int len)
- {
- unsigned int reg = 0; // 0xFFFFFFFF,见后面解释
- for(int i = 0; i < len; i++)
- {
- reg = (reg<<8) ^ crc_table[(reg&0xFF) ^ data[i]];
- return reg;
- }
- }
- // 最终生成的校验表将是:
- // {0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
- // 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
- // … …}
- // 字节逆转前的CRC32算法,字节序为b8,b7,…,b1
- #define POLY 0x04C11DB7L // CRC32生成多项式
- static unsigned int crc_table[256];
- unsigned int get_sum_poly(unsigned char data)
- {
- unsigned int sum_poly = data;
- sum_poly <<= 24;
- for(int j = 0; j < 8; j++)
- {
- int hi = sum_poly&0x80000000; // 取得reg的最高位
- sum_poly <<= 1;
- if(hi) sum_poly = sum_poly^POLY;
- }
- return sum_poly;
- }
- void create_crc_table()
- {
- for(int i = 0; i < 256; i++)
- {
- crc_table[i] = get_sum_poly(i&0xFF);
- }
- }
- unsigned int CRC32_4(unsigned char* data, int len)
- {
- unsigned int reg = 0;// 0xFFFFFFFF,见后面解释
- for(int i = 0; i < len; i++)
- {
- reg = (reg<<8) ^ crc_table[(reg>>24)&0xFF ^ data[i]];
- return reg;
- }
- }
长征结束了
到这里长征终于结束了, 事实上,还有最后的一小步,那就是 reg 初始值的问题,上面的算法中 reg 初始值为 0 。在一些传输协议中,发送端并不指出消息长度,而是采用结束标志,考虑下面的这几种可能的差错:
1 )在消息之前,增加 1 个或多个 0 字节;
2) 在消息 ( 包括校验码 ) 之后,增加 1 个或多个 0 字节;
显然,这几种差错都检测不出来,其原因就是如果 reg=0 ,处理 0 消息字节 ( 或位 ) , reg 的值保持不变。解决这种问题也很简单,只要使 reg 的初始值非 0 即可,一般取 0Xffffffff ,就像你在很多 CRC32 实现中发现的那样。
到这里终于可以松一口气了, CRC32 并不是像想象的那样容易的算法啊!事实上还真不容易!这就叫做“简单的前面是优雅,背后是复杂”!