概述
文章目录
- 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,1x8,bytes[i] xor leftmost 8 bits of rem
对应
γ
1
,
4
+
B
0
gamma_{1,4} + B_{0}
γ1,4+B0,crc_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 Bn−1x8(n−1)+⋯+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 Bn−1x8(n−1)+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} Pn−1=Bn−1x8(n−1)+32+Pn−2 ,其中 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} Pn−2=Bn−2x8(n−2)+32+Bn−3x8(n−3)+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}
Bn−1x8(n−1)+32+Bn−2x8(n−2)+32+⋯+B1x40+B0x32modC=(Bn−1x8(n−1)+32+Pn−2)modC=(Bn−1x8(n−1)+32modC+Pn−2)modC=((Bn−1x32modC)x8(n−1)+Pn−2)modC
其中
B
n
−
1
x
32
m
o
d
C
B_{n-1} x^{32} bmod C
Bn−1x32modC 可以通过查表获取,其结果为一个 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
γn−1,4x24+γn−1,3x16+γn−1,2x8+γn−1,1x0,其中
γ
n
−
1
,
i
(
i
=
1
,
2
,
3
,
4
)
gamma_{n-1,i} (i=1,2,3,4)
γn−1,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}
Rn−1=Pn−1modC=Bn−1x8(n−1)+32+Bn−2x8(n−2)+32+⋯+B2x40+B1x32modC=((Bn−1x32modC)x8(n−1)+Pn−2)modC=((γn−1,4x24+γn−1,3x16+γn−1,2x8+γn−1,1x0)x8(n−1)+Pn−2)modC
最后一个等式的被除数实际上是一个
n
−
2
n-2
n−2 阶的多项式(八个数字为一个单位,附加的 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}
{Pn−1modC=((γn−1,4x24+γn−1,3x16+γn−1,2x8+γn−1,1x0)x8(n−1)+Pn−2)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} (γn−1,4x24+γn−1,3x16+γn−1,2x8+γn−1,1x0)x8(n−1)+Pn−2 的前四个字节。另外前导 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}
Rn−1=Pn−1modC=Bn−1x8(n−1)+32+Bn−2x8(n−2)+32+⋯+B2x40+B1x32modC=((Bn−1x32modC)x8(n−1)+Pn−2)modC=((γn−1,4x24+γn−1,3x16+γn−1,2x8+γn−1,1x0)x8(n−1)+Pn−2)modC=(((γn−1,4+Bn−2)x24+γn−1,3x16+γn−1,2x8+γn−1,1x0)x8(n−1)+Pn−3)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+pn∣∀pn∈Pn} ,这是一个递归定义(而且是尾递归可以用循环体优化)。
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)x−C,如果小于 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)x−C=(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 校验查表法的内在原理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复