CRC--简单介绍

1. 简介

CRC即循环冗余校验。是一种来验证数据是否出现差错的一种算法

2. 引入

如何检验一串数据出现了差错呢?

2.1 加法

可以将所有这些数据分块相加然后再模上一个数,得到最后的checksum
但是这样做的缺点是,可能值已经改变了多次,但是checksum却相同。
如:
数据 24 23 ,checksum47
但若数据变为 25 22,checksum却并没有发生改变。
所以用累加求和的方式来检验数据的差错性并不理想。

CRC处理差错的方式是除法

2.2 除法

再计算机我们用二进制来表示一个数;

为了方便我们计算,我们直接忽略掉产生的最高位进位。

这样对于二进制的加减法,我们都可以用异或来进行计算。

做了这样操作后,实际上是没有大小这一说了。

而对于乘法,则是一个数循环左移最后再进行上述加法的过程。

同样为了方便计算,我们做除法时只考虑最高位是否大于除数即可。

2.3 除数的选择

除数的选择,这些都是数学家们的事。

他们有个名字叫做生成多项式。

这个多项式具体如何选择能取得更好的效果,也是数学家的事情。

我们要产生的是二进制的checksum,自然多项式的形式应该为

∑ i = 0 n v i ∗ 2 i , v i = 0 , 1 \sum_{i=0}^{n}v_i *2^{i},v_i = 0,1 i=0nvi2i,vi=0,1

如果你对如何选择多项式不感兴趣可以直接看实现上,可以直接跳到实现上。

假设原来的数据为 D D D,若出现差错了;则差错序列为 E E E,则一定存在一个序列 T T T使得
D ⊕ T = E D \oplus T =E DT=E
且序列 D ! = 0 D != 0 D!=0,一定发生了差错。
为了能够检测到错误,生成多项式 P P P一定不能被 D D D二进制整除(上面所叙述的)。

那么有这么几条:

  1. 为了检测单个比特位的改变,只需要 P P P中二进制表示中1的位数超过1即可。
    因为无论 P P P怎样进行循环左移,最后的二进制表示中1的位数都一定是超过1的,所以能检测到单个比特位发生的变化
  2. 为了检测两个比特位的改变, P P P的形式不能为 11   101   1001... 11 \ 101 \ 1001 ... 11 101 1001...的倍数(循环左移)。
    这个也很好理解,假设原串的两个比特位为0, 现在都变为了1,而这两个1的距离又刚好与 P P P中的两个1的距离一样不就使得 P ∣ E P | E PE了吗。
  3. 奇数个二进制的1位的 E E E一定不能被偶数个二进制位1 P P P所整除,即可以检测到。
  4. 一堆连续的1的差错出现可以使得 P P P的最低位为1来解决。

根据这几个原则,最后选择的多项式

16 12 5 0
16 15 2 0
32 26 23 22 16 12 11 10 8 7 5 4 2 1 0

3. 实现

3.1 简单实现
 while (len--) 
 { 
 	byte t = (r >> 24) & 0xFF; 
 	r = (r << 8) | *p++; 
 	r^=table[t]; 
 }
// 
 r=0; while (len--) r = ((r << 8) | *p++) ^ t[(r >> 24) & 0xFF];
3.2 标准实现
for (unsigned int i = 0; i <= 0xFF; i++)
{
  uint32_t crc = i;
  for (unsigned int j = 0; j < 8; j++)
    crc = (crc >> 1) ^ (-int(crc & 1) & Polynomial);
  Crc32Lookup[i] = crc;
}
uint32_t crc32_1byte(const void* data, size_t length, uint32_t previousCrc32 = 0)
{
  uint32_t crc = ~previousCrc32;
  unsigned char* current = (unsigned char*) data;
  while (length--)
    crc = (crc >> 8) ^ Crc32Lookup[(crc & 0xFF) ^ *current++];
  return ~crc;
}
3.3 现代cpu优化

4字节或者8字节处理一次

// same as before
for (unsigned int i = 0; i <= 0xFF; i++)
{
  uint32_t crc = i;
  for (unsigned int j = 0; j < 8; j++)
    crc = (crc >> 1) ^ ((crc & 1) * Polynomial);
  Crc32Lookup[0][i] = crc;
}
for (unsigned int i = 0; i <= 0xFF; i++)
{
  // for Slicing-by-4 and Slicing-by-8
  Crc32Lookup[1][i] = (Crc32Lookup[0][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[0][i] & 0xFF];
  Crc32Lookup[2][i] = (Crc32Lookup[1][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[1][i] & 0xFF];
  Crc32Lookup[3][i] = (Crc32Lookup[2][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[2][i] & 0xFF];
  // only Slicing-by-8
  Crc32Lookup[4][i] = (Crc32Lookup[3][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[3][i] & 0xFF];
  Crc32Lookup[5][i] = (Crc32Lookup[4][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[4][i] & 0xFF];
  Crc32Lookup[6][i] = (Crc32Lookup[5][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[5][i] & 0xFF];
  Crc32Lookup[7][i] = (Crc32Lookup[6][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[6][i] & 0xFF];
}

Ref

a_painless_crc
fast_crc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值