是的。您可以在zlib 的crc32_combine() 中看到如何。如果你有两个序列A和B,那么AB的纯CRC是A0的CRC和0B的CRC的异或,其中0代表一系列具有相应序列长度的零字节,即B和 A。
对于您的应用程序,您可以预先计算一个运算符,该运算符非常快速地将 1020 个零应用于前四个字节的 CRC。然后你可以用预先计算的 1020 字节的 CRC 进行异或。
更新:
这是我 2008 年的一篇文章,其中包含@ArtemB 发现的详细解释(我已经忘记了):
zlib 中的crc32_combine() 基于两个关键技巧。对于接下来的内容,
我们搁置了标准 32 位 CRC 是前和后的事实
有条件的。我们可以稍后处理。现在假设一个 CRC
没有这样的条件,所以从填充的寄存器开始
零。
技巧 #1:CRC 是线性的。因此,如果您有流 X 和流 Y
相同的长度和异或两个流逐位得到Z,
即 Z = X ^ Y(使用 C 表示法进行异或),然后 CRC(Z) =
CRC(X) ^ CRC(Y)。对于手头的问题,我们有两个流 A 和 B
我们想要连接到流 Z 中的不同长度的。什么
我们有 CRC(A) 和 CRC(B)。我们想要的是一个快速的方法
计算 CRC(Z)。诀窍是构造 X = A 连接
长度(B)零位,并且 Y = 长度(A)零位与 B 连接。
因此,如果我们仅通过并列
符号,X = A0,Y = 0B,然后 X^Y = Z = AB。然后我们有 CRC(Z) =
CRC(A0) ^ CRC(0B)。
现在我们需要知道 CRC(A0) 和 CRC(0B)。 CRC(0B) 很简单。如果我们喂
CRC机器的一堆零,从零开始,寄存器
仍然用零填充。所以就好像我们什么都没做一样。
因此 CRC(0B) = CRC(B)。
CRC(A0) 然而需要更多的工作。采用非零 CRC 并喂食
CRC机器的零不会让它不管。每零变化
寄存器内容。所以要得到CRC(A0),我们需要设置寄存器
到CRC(A),然后运行长度(B)通过它为零。然后我们可以
异或 CRC(B) = CRC(0B) 的结果,我们得到什么
我们想要,即 CRC(Z) = CRC(AB)。瞧!
嗯,事实上,瞧,还为时过早。我一点都不满意
那个答案。我不想要一个需要时间的计算
与 B 的长度成正比。与此相比,这不会节省任何时间
只需将寄存器设置为 CRC(A) 并运行 B 流
通过。我认为必须有一种更快的方法来计算效果
将 n 个零输入 CRC 机器(其中 n = length(B))。所以
这导致我们:
技巧 #2:CRC 机器是线性状态机。如果我们知道
当我们向机器输入零时发生的线性变换,
然后我们可以更有效地对该转换进行操作
找到将 n 个零输入到
机器。
将单个零位输入 CRC 机器的转换
完全由一个 32x32 的二进制矩阵表示。要应用
变换我们将矩阵乘以寄存器,取
注册为 32 位列向量。对于矩阵乘法
二进制(即超过 2 的伽罗瓦域),乘法的作用
由and'ing扮演,加法的角色由exclusive-扮演
或'ing。
有几种不同的方法可以构建魔法矩阵
表示给 CRC 机器输入 a 引起的变换
单个零位。一种方法是观察矩阵的每一列
是当您的注册以一个单一的开始时您得到的
它。所以第一列是当寄存器为 100 时你得到的...
然后输入一个零,第二列来自于
0100...等(这些被称为基向量。)你可以看到
这只需对这些向量进行矩阵乘法即可。
矩阵乘法选择矩阵的列
对应单的位置。
现在是诀窍。一旦我们有了魔法矩阵,我们就可以搁置一旁
一段时间的初始寄存器内容,而不是使用
一个零的转换以计算 n 的转换
零。我们可以将矩阵的 n 个副本相乘得到
n 个零的矩阵。但这比仅仅运行 n 还要糟糕
通过机器归零。但是有一个简单的方法可以避免大多数
这些矩阵乘法得到相同的答案。假设我们
想知道运行八个零位或一位的转换
字节通过。我们称其为代表运行的魔法矩阵
零到:M。我们可以做七次矩阵乘法得到 R =
MxMxMxMxMxMxMxM。相反,让我们从 MxM 开始并称其为 P。然后
PxP 是 MxMxMxM。我们称它为 Q。那么 QxQ 就是 R。所以现在我们有了
将七个乘法减少到三个。 P = MxM,Q = PxP,R =
QxQ。
现在我确定您已经了解了任意 n 个零的想法。我们
可以非常快速地生成变换矩阵 Mk,其中 Mk 是
运行 2k 个零的转换。 (在里面
上面的段落 M3 是 R。)我们可以只用 k 使 M1 到 Mk
矩阵乘法,从 M0 = M 开始。k 只需为
与 n 的二进制表示中的位数一样大。我们可以
然后选择那些在二进制中有矩阵的矩阵
n 的表示并将它们相乘得到
通过 CRC 机器运行 n 个零的转换。所以如果 n =
13、计算M0 x M2 x M3.
如果j是n的二进制表示中1的个数,那么我们
只需 j - 1 个矩阵乘法。所以我们总共有 k
+ j - 1 次矩阵乘法,其中 j k = floor(logbase2(n))。
现在我们将快速构建的矩阵用于 n 个零,然后相乘
通过 CRC(A) 得到 CRC(A0)。我们可以在 O(log(n)) 中计算 CRC(A0)
时间,而不是 O(n) 时间。我们独家或与 CRC(B) 和
瞧! (这次真的),我们有 CRC(Z)。
这就是 zlib 的 crc32_combine() 所做的。
我将把它作为练习留给读者如何处理
CRC 寄存器的预处理和后处理。你只需要
应用上面的线性观察。提示:你不需要知道
长度(A)。事实上crc32_combine() 只需要三个参数:
CRC(A)、CRC(B) 和长度(B)(以字节为单位)。