我是靠谱客的博主 微笑老师,最近开发中收集的这篇文章主要介绍CRC 校验查表法的内在原理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

      • CRC 余数的计算
      • CRC 表的生成
      • bit 序和 CRC 多项式的逆转
      • 带有初始值的 CRC 求余

CRC 余数的计算

设 CRC 多项式为 C C C

先只说明两个字节的情况(最高有效位在左侧),大于两个字节(16 位数)的可以类推。注意下文中的所有加法都是无进位加法,也就是二进制中的异或。

表中数据为单字节(8 bit)的 CRC 余数。将被除数多项式每八位为一组,设为 B 1 x 8 + B 0 x 0 B_{1} x^8 + B_0x^0 B1x8+B0x0

其中 B 1 , B 0 B_{1}, B_0 B1,B0 x x x 进制的八位数。

已知 B 1 x 32   m o d   C = R 1 B_{1} x^{32} bmod C= R_1 B1x32modC=R1 R 1 R_1 R1 为 32 位的余数。

注意下文中的 Γ x 32   m o d   C Gamma x^{32} bmod C Γx32modC 均可以直接查表获得(以 Γ Gamma Γ 为 CRC 表的索引)。

( B 1 x 8 + B 0 x 0 ) x 32   m o d   C (B_{1} x^8 + B_{0}x^0)x^{32} bmod C (B1x8+B0x0)x32modC 的值:

等价于 ( B 1 x 40 + B 0 x 32 )   m o d   C = ( R 1 x 8 + B 0 x 32 )   m o d   C (B_{1} x^{40} + B_{0} x^{32}) bmod C = (R_1 x^8 + B_{0}x^{32}) bmod C (B1x40+B0x32)modC=(R1x8+B0x32)modC

R 1 = γ 1 , 4 x 24 + γ 1 , 3 x 16 + γ 1 , 2 x 8 + γ 1 , 1 x 0 R_1 = gamma_{1,4} x^{24} + gamma_{1,3} x^{16} + gamma_{1,2} x^{8} + gamma_{1,1}x^0 R1=γ1,4x24+γ1,3x16+γ1,2x8+γ1,1x0,其中 γ 1 , i   ( i = 1 , 2 , 3 ) gamma_{1,i} (i=1,2,3) γ1,i (i=1,2,3) 为 8 位数。

所以:
( B 1 x 40 + B 0 x 32 )   m o d   C = ( ( B 1 x 32   m o d   C ) x 8 + B 0 x 32 )   m o d   C = ( R 1 x 8 + B 0 x 32 )   m o d   C = ( γ 1 , 4 x 32 + γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 + B 0 x 32 )   m o d   C = ( ( γ 1 , 4 + B 0 ) x 32 + γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 )   m o d   C = ( ( γ 1 , 4 + B 0 ) x 32   m o d   C + γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 )   m o d   C begin{aligned} &(B_1 x^{40} + B_0x^{32}) bmod C \ &= ((B_1 x^{32} bmod C) x^8 + B_0x^{32})bmod C \ &= (R_1 x^8 + B_{0}x^{32}) bmod C \ &=(gamma_{1,4} x^{32} + gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8 + B_{0} x^{32}) bmod C \ &= ((gamma_{1,4} + B_{0}) x^{32} + gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8) bmod C \ &= ((gamma_{1,4} + B_{0}) x^{32} bmod C + gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8)bmod C end{aligned} (B1x40+B0x32)modC=((B1x32modC)x8+B0x32)modC=(R1x8+B0x32)modC=(γ1,4x32+γ1,3x24+γ1,2x16+γ1,1x8+B0x32)modC=((γ1,4+B0)x32+γ1,3x24+γ1,2x16+γ1,1x8)modC=((γ1,4+B0)x32modC+γ1,3x24+γ1,2x16+γ1,1x8)modC
注意到最后一个等式中的被除数已经是 32 位数了,所以可得:
( B 1 x 40 + B 0 x 32 )   m o d   C = ( ( γ 1 , 4 + B 0 ) x 32   m o d   C + γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 )   m o d   C = ( γ 1 , 4 + B 0 ) x 32   m o d   C + γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 begin{aligned} &(B_1 x^{40} + B_0x^{32}) bmod C \ &= ((gamma_{1,4} + B_{0}) x^{32} bmod C + gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8)bmod C \ &= (gamma_{1,4} + B_{0}) x^{32} bmod C + gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8 end{aligned} (B1x40+B0x32)modC=((γ1,4+B0)x32modC+γ1,3x24+γ1,2x16+γ1,1x8)modC=(γ1,4+B0)x32modC+γ1,3x24+γ1,2x16+γ1,1x8
其中 ( γ 1 , 4 + B 0 ) x 32   m o d   C (gamma_{1,4} + B_{0}) x^{32} bmod C (γ1,4+B0)x32modC 可以通过查表获得。转换成代码:

`rem = (rem leftshift 8) xor crc_table[bytes[i] xor leftmost 8 bits of rem]

rem leftshift 8 对应 γ 1 , 3 x 24 + γ 1 , 2 x 16 + γ 1 , 1 x 8 gamma_{1,3} x^{24} + gamma_{1,2} x^{16} + gamma_{1,1} x^8 γ1,3x24+γ1,2x16+γ1,1x8bytes[i] xor leftmost 8 bits of rem 对应 γ 1 , 4 + B 0 gamma_{1,4} + B_{0} γ1,4+B0crc_table[bytes[i] xor leftmost 8 bits of rem] 则对应 ( γ 1 , 4 + B 0 ) x 32   m o d   C (gamma_{1,4} + B_{0}) x^{32} bmod C (γ1,4+B0)x32modC

现在处理大于 16 位数的情况:

设被除数为 B n − 1 x 8 ( n − 1 ) + ⋯ + B 1 x 8 + B 0 x 0 B_{n-1} x^{8(n-1)} + cdots + B_1 x^8 + B_0 x^0 Bn1x8(n1)++B1x8+B0x0

根据 CRC 算法,左移 32 位,也就是求 B n − 1 x 8 ( n − 1 ) + 32 + ⋯ + B 1 x 40 + B 0 x 32   m o d   C B_{n-1} x^{8(n-1)+32} + cdots + B_1 x^{40} + B_0 x^{32} bmod C Bn1x8(n1)+32++B1x40+B0x32modC

P n − 1 = B n − 1 x 8 ( n − 1 ) + 32 + P n − 2 P_{n-1}=B_{n-1} x^{8(n-1) + 32} + P_{n-2} Pn1=Bn1x8(n1)+32+Pn2 ,其中 P n − 2 = B n − 2 x 8 ( n − 2 ) + 32 + B n − 3 x 8 ( n − 3 ) + 32 + ⋯ B 0 x 32 P_{n-2}=B_{n-2} x ^{8(n-2) + 32} + B_{n-3}x^{8(n-3) + 32} + cdots B_0 x^{32} Pn2=Bn2x8(n2)+32+Bn3x8(n3)+32+B0x32


B n − 1 x 8 ( n − 1 ) + 32 + B n − 2 x 8 ( n − 2 ) + 32 + ⋯ + B 1 x 40 + B 0 x 32   m o d   C = ( B n − 1 x 8 ( n − 1 ) + 32 + P n − 2 )   m o d   C = ( B n − 1 x 8 ( n − 1 ) + 32   m o d   C + P n − 2 )   m o d   C = ( ( B n − 1 x 32   m o d   C ) x 8 ( n − 1 ) + P n − 2 )   m o d   C begin{aligned} & B_{n-1} x^{8(n-1)+32} + B_{n-2} x^{8(n-2)+32} + cdots + B_1 x^{40} + B_0 x^{32} bmod C \ &= (B_{n-1} x^{8(n-1) + 32} + P_{n-2}) bmod C \ &= (B_{n-1}x^{8(n-1) + 32} bmod C + P_{n-2}) bmod C \ &= ((B_{n-1} x^{32} bmod C)x^{8(n-1)} + P_{n-2}) bmod C end{aligned} Bn1x8(n1)+32+Bn2x8(n2)+32++B1x40+B0x32modC=(Bn1x8(n1)+32+Pn2)modC=(Bn1x8(n1)+32modC+Pn2)modC=((Bn1x32modC)x8(n1)+Pn2)modC
其中 B n − 1 x 32   m o d   C B_{n-1} x^{32} bmod C Bn1x32modC 可以通过查表获取,其结果为一个 32 位的数字设为 γ n − 1 , 4 x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 gamma_{n-1,4} x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0 γn1,4x24+γn1,3x16+γn1,2x8+γn1,1x0,其中 γ n − 1 , i   ( i = 1 , 2 , 3 , 4 ) gamma_{n-1,i} (i=1,2,3,4) γn1,i (i=1,2,3,4) 为 8 位的数字。

所以可得:
R n − 1 = P n − 1   m o d   C = B n − 1 x 8 ( n − 1 ) + 32 + B n − 2 x 8 ( n − 2 ) + 32 + ⋯ + B 2 x 40 + B 1 x 32   m o d   C = ( ( B n − 1 x 32   m o d   C ) x 8 ( n − 1 ) + P n − 2 )   m o d   C = ( ( γ n − 1 , 4 x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 ) x 8 ( n − 1 ) + P n − 2 )   m o d   C begin{aligned} R_{n-1} = P_{n-1}bmod C &= B_{n-1} x^{8(n-1)+32} + B_{n-2} x^{8(n-2)+32} + cdots + B_2 x^{40} + B_1 x^{32} bmod C \ &= ((B_{n-1} x^{32} bmod C)x^{8(n-1)} + P_{n-2}) bmod C \ &= ((gamma_{n-1,4} x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0) x^{8(n-1)}+ P_{n-2}) bmod C \ end{aligned} Rn1=Pn1modC=Bn1x8(n1)+32+Bn2x8(n2)+32++B2x40+B1x32modC=((Bn1x32modC)x8(n1)+Pn2)modC=((γn1,4x24+γn1,3x16+γn1,2x8+γn1,1x0)x8(n1)+Pn2)modC
最后一个等式的被除数实际上是一个 n − 2 n-2 n2 阶的多项式(八个数字为一个单位,附加的 4 个 8 位数不算在内)

可得关系式:
{ P n − 1   m o d   C = ( ( γ n − 1 , 4 x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 ) x 8 ( n − 1 ) + P n − 2 )   m o d   C P 0   m o d   C = B 0 x 32   m o d   C begin{cases} P_{n-1} bmod C = ((gamma_{n-1,4} x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0) x^{8(n-1)}+ P_{n-2}) bmod C \ P_0 bmod C= B_0 x^{32} bmod C\ end{cases} {Pn1modC=((γn1,4x24+γn1,3x16+γn1,2x8+γn1,1x0)x8(n1)+Pn2)modCP0modC=B0x32modC
可以直接将递归式展开:
P 0   m o d   C = B 0 x 32   m o d   C P 1   m o d   C = ( ( γ 1 , 4 x 24 + γ 1 , 3 x 16 + γ 1 , 2 x 8 + γ 1 , 1 x 0 ) x 8 + P 0 )   m o d   C P 2   m o d   C = ( ( γ 2 , 4 x 24 + γ 2 , 3 x 16 + γ 2 , 2 x 8 + γ 2 , 1 x 0 ) x 16 + P 1 )   m o d   C ⋯ begin{aligned} &P_0 bmod C= B_0 x^{32} bmod C \ &P_{1} bmod C = ((gamma_{1,4} x^{24} + gamma_{1,3}x^{16} + gamma_{1,2} x^8 + gamma_{1,1}x^0) x^{8}+ P_{0}) bmod C \ &P_{2} bmod C= ((gamma_{2,4} x^{24} + gamma_{2,3}x^{16} + gamma_{2,2} x^8 + gamma_{2,1}x^0) x^{16}+ P_{1}) bmod C \ &cdots end{aligned} P0modC=B0x32modCP1modC=((γ1,4x24+γ1,3x16+γ1,2x8+γ1,1x0)x8+P0)modCP2modC=((γ2,4x24+γ2,3x16+γ2,2x8+γ2,1x0)x16+P1)modC

如果使用递归函数:

// parameter bytes is append with 4 empty bytes;
fn crc_remainder(bytes: mut Vec<u8>) -> u32 {
let mut remainder:u32 = match bytes.pop {
Some(b_n_1) => crc_table[b_n_1],
None => 0,
};
let mut new_bytes: Vec<u8> = vec![remainder >> 24, remainder >> 16 & 0xff, remainder >> 8 & 0xff, remainder & 0xff];
let mut i = 0;
while let Some(top) = bytes.top() {
new_bytes[i] ^= top;
i += 1;
}
if i <= 4 {
return new_bytes[0] << 24 | new_bytes[1] << 16 | new_bytes[2] << 8 | new_bytes[3];
}
new_bytes.append(bytes);
return crc_remainder(new_bytes);
}

这种递归式主要缺点在于修改了原始数据,而且内存调整非常消耗资源。而尾调用可以很容易的修改为循环结构。为了不修改原始数据,可以使用移位寄存器,用来保存 ( γ n − 1 , 4 x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 ) x 8 ( n − 1 ) + P n − 2 (gamma_{n-1,4} x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0) x^{8(n-1)}+ P_{n-2} (γn1,4x24+γn1,3x16+γn1,2x8+γn1,1x0)x8(n1)+Pn2 的前四个字节。另外前导 0 0 0 并不影响结果。

fn crc_remain(bytes: &[u8]) -> u32 {
let mut shift_register:u32 = 0;
let mut i = 0;
while i < bytes.len {
shift_register = (shift_register << 8) | bytes[i];
shift_register = (shift_register << 8) ^ crc_table[shift_register >> 24];
i += 1;
}
shift_register
}

前四次移位实际上没有任何效果,只是将移位寄存器填满,如果将前面的递推关系改写成下面这种形式:
R n − 1 = P n − 1   m o d   C = B n − 1 x 8 ( n − 1 ) + 32 + B n − 2 x 8 ( n − 2 ) + 32 + ⋯ + B 2 x 40 + B 1 x 32   m o d   C = ( ( B n − 1 x 32   m o d   C ) x 8 ( n − 1 ) + P n − 2 )   m o d   C = ( ( γ n − 1 , 4 x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 ) x 8 ( n − 1 ) + P n − 2 )   m o d   C = ( ( ( γ n − 1 , 4 + B n − 2 ) x 24 + γ n − 1 , 3 x 16 + γ n − 1 , 2 x 8 + γ n − 1 , 1 x 0 ) x 8 ( n − 1 ) + P n − 3 )   m o d   C begin{aligned} R_{n-1} = P_{n-1}bmod C &= B_{n-1} x^{8(n-1)+32} + B_{n-2} x^{8(n-2)+32} + cdots + B_2 x^{40} + B_1 x^{32} bmod C \ &= ((B_{n-1} x^{32} bmod C)x^{8(n-1)} + P_{n-2}) bmod C \ &= ((gamma_{n-1,4} x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0) x^{8(n-1)}+ P_{n-2}) bmod C \ & = (((gamma_{n-1,4} + B_{n-2}) x^{24} + gamma_{n-1,3}x^{16} + gamma_{n-1,2} x^8 + gamma_{n-1,1}x^0) x^{8(n-1)} + P_{n-3}) bmod C end{aligned} Rn1=Pn1modC=Bn1x8(n1)+32+Bn2x8(n2)+32++B2x40+B1x32modC=((Bn1x32modC)x8(n1)+Pn2)modC=((γn1,4x24+γn1,3x16+γn1,2x8+γn1,1x0)x8(n1)+Pn2)modC=(((γn1,4+Bn2)x24+γn1,3x16+γn1,2x8+γn1,1x0)x8(n1)+Pn3)modC
则有:

fn crc_remainder(bytes: &[u8]) -> u32 {
let mut shift_register:u32 = 0;
let mut i = 0;
while i < bytes.len {
shift_register ^= bytes[i] << 24; // 上式中的最后一个等式
shift_register = crc_table[shift_register >> 24] ^ (shift_register << 8);
i += 1;
}
}

这实际上就是开头所讲的:

rem = (rem leftshift 8) xor crc_table[bytes[i] xor leftmost 8 bits of rem]

如果严格按照上面递推关系式编写代码,如下所示:

fn crc_remainder(bytes: &[u8]) -> u32 {
if bytes.len == 0 {
return 0;
}
let mut shift_register:u32 = crc_table[bytes[0]]; // 递推关系式第二行、第三行等式
let mut i = 1;
while i < bytes.len {
// 上式中的最后一个等式。
shift_register ^= bytes[i] << 24;
// 将最后一个等式中的被除数作为 P_{n-2},将下一个循环提前。递推关系式的第二、三行。
shift_register = crc_table[shift_register >> 24] ^ (shift_register << 8);
i += 1;
}
}

和上一个代码片段在操作步骤上实际上是等价的。

CRC 表的生成

如果按字节索引,实际上就是生成 [ 0 , 255 ] [0,255] [0,255] 范围内所有整型的 CRC 余数,单字节可以直接移位处理计算。值得注意的是: ( B 1 + B 2 )   m o d   C = ( B 1   m o d   C + B 2   m o d   C )   m o d   C (B_1 + B_2) bmod C = (B_1 bmod C + B_2 bmod C)bmod C (B1+B2)modC=(B1modC+B2modC)modC ,由于我们实施的是无进位加法,所以 B 1   m o d   C + B 2   m o d   C B_1 bmod C + B_2 bmod C B1modC+B2modC 的位数是小于 C C C 的,从而 ( B 1 + B 2 )   m o d   C = B 1   m o d   C + B 2   m o d   C (B_1 + B_2) bmod C = B_1 bmod C + B_2 bmod C (B1+B2)modC=B1modC+B2modC

也就是说,我们计算了 a , b ∈ [ 0 , 255 ] a,b in [0,255] a,b[0,255] 的 CRC 余数,那么就可以直接通过 ( B 1 + B 2 )   m o d   C = B 1   m o d   C + B 2   m o d   C (B_1 + B_2) bmod C = B_1 bmod C + B_2 bmod C (B1+B2)modC=B1modC+B2modC 关系获取到 c = a + b ∈ [ 0 , 255 ] c=a+b in [0,255] c=a+b[0,255] 的 CRC 余数,这是一个加快生成 CRC 表的技巧。

需要注意的是这里的加法在编码的时候要变成异或。

前文也提到过这一点。

设最高阶为 n n n 的多项式集合为 P n P_n Pn,那么 P n + 1 = { a n + 1 x n + 1 + p n ∣ ∀ p n ∈ P n } P_{n+1} = {a_{n+1}x^{n+1} + p_{n}mid forall p_nin P_{n}} Pn+1={an+1xn+1+pnpnPn} ,这是一个递归定义(而且是尾递归可以用循环体优化)。

P 0 = { 0 } P_0={0} P0={0} 是递归定义的初始条件。 也就是说我们只需要求出 a k x k ( k = 1 , 2 , 3 ⋯   ) a_{k}x^k (k=1,2,3cdots) akxk(k=1,2,3)   m o d   C bmod C modC 的余数,就可以通过递归式求出 P k P_k Pk 中所有多项式   m o d   C bmod C modC 的余数。

转换成二进制,求 8 bit 中所有数值的 CRC 余数,实际上我们只需要求出 0 , 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 0,1,2,4,8,16,32,64,128 0,1,2,4,8,16,32,64,128 的 CRC 余数即可。( 0 0 0 的 CRC 余数即为 0 0 0,该数值的 CRC 余数无需通过移位计算。)

x n + 1 x 32   m o d   C = ( ( x n x 32   m o d   C ) x )   m o d   C x^{n+1}x^{32}bmod C = ((x^nx^{32}bmod C)x)bmod C xn+1x32modC=((xnx32modC)x)modC ,而 ( x n x 32   m o d   C ) x (x^nx^{32}bmod C)x (xnx32modC)x 的阶数是小于等于 C C C 的阶数的。如果等于 C C C 的阶数,则结果等于 ( x n x 32   m o d   C ) x − C (x^nx^{32}bmod C)x-C (xnx32modC)xC,如果小于 C C C 的阶数,则结果为 ( x n x 32   m o d   C ) x (x^nx^{32}bmod C)x (xnx32modC)x。也就是说我们计算了 2 n   m o d   C 2^n bmod C 2nmodC 之后,观察其最高位是否为 1;如果是 1,则 + C +C +C,否则 + 0 +0 +0

注意 C C C 是 33 位,且最高位为 1 1 1,所以 CRC-32 多项式一般将最高位省略,用 32 个 bit 来表示 33 个 bit。

二进制无进位加法等同于无进位减法,均表示为异或操作。所以 ( x n x 32   m o d   C ) x − C = ( x n x 32   m o d   C ) x + C (x^nx^{32}bmod C)x-C = (x^nx^{32}bmod C)x + C (xnx32modC)xC=(xnx32modC)x+C

利用该方法,我们可以通过 8 次移位便可以将 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 1,2,4,8,16,32,64,128 1,2,4,8,16,32,64,128 的 CRC 余数计算出来,而无需将每一个数值当作一个 8 bit 的字节来进行计算。耗时约等于原来的 1 8 frac 18 81

总耗时大概是 8 次移位和最多 256 次的异或操作。所以表格的生成是相当快速的,甚至可以在运行时进行。

bit 序和 CRC 多项式的逆转

  • to-do

带有初始值的 CRC 求余

  • to-do

最后

以上就是微笑老师为你收集整理的CRC 校验查表法的内在原理的全部内容,希望文章能够帮你解决CRC 校验查表法的内在原理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部