背景:
我有一段1024字节的内存。最后1020个字节将始终相同。前4个字节将改变(产品的序列号)。我需要为整个内存部分CRC-16
计算CCITT
CRC_WHOLE
(0xFFFF起始,0x1021掩码)。
问题:
是否可以仅计算前4个字节CRC_A
的CRC,然后应用如下所示的函数来计算完整的CRC?我们可以假设最后1020个字节CRC_B
的校验和已经知道。
CRC_WHOLE = XOR(CRC_A, CRC_B)
我知道这个公式不起作用(尝试过),但我希望存在类似的东西。
答案 0 :(得分:27)
是。您可以在zlib' s 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机器的一堆零 仍然充满了零。所以,好像我们什么也没做。 因此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 =长度(B))。所以 这导致我们:
技巧#2:CRC机器是线性状态机。如果我们知道 当我们向机器提供零时发生的线性变换, 然后我们可以更有效地对转换进行操作 找到将 n 零加入的结果转化为 机。
将单个零位馈入CRC机器的转换 完全由32x32二进制矩阵表示。申请 转换我们将矩阵乘以寄存器,取 注册为32位列向量。对于矩阵乘法 二进制(即超过Galois Field of 2),乘法的作用 由...播放,并且添加的作用由独家播放 - 或' 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个零的想法。我们 可以非常快速地生成转换矩阵M k ,其中M k 是 运行2 k 零的转换。 (在里面 M 3 之上的段落是R.)我们可以通过M k 使M 1 只有 k 矩阵乘法,从M 0 = M. k 开始,只需要为 大于 n 的二进制表示中的位数。我们可以 然后选择那些二进制中存在的矩阵 表示 n 并将它们相乘以得到 转换通过CRC机器运行 n 零。所以如果 n = 13,计算M 0 x M 2 x M 3 。
如果 j 是 n 的二进制表示中的一个数,那么我们 只需要 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' s crc32_combine()
的作用。
我将把它留作读者如何处理的练习
CRC寄存器的前后调节。你只需要
应用上面的线性观察。提示:你不需要知道
长度(A)。实际上crc32_combine()
只有三个参数:
CRC(A),CRC(B)和长度(B)(以字节为单位)。
答案 1 :(得分:1)
下面是用于CRC(A0)的另一种方法的示例C代码。如果不使用矩阵,则可以通过对(CRC·((2 ^ n)%POLY)%POLY进行多重运算将CRC向前循环n位,因此对整数而不是矩阵执行重复平方。如果n为常数,则可以预先计算(2 ^ n)%POLY。
/* crcpad.c - crc - data has a large number of trailing zeroes */
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
#define POLY (0x04c11db7u)
static uint32_t crctbl[256];
void GenTbl(void) /* generate crc table */
{
uint32_t crc;
uint32_t c;
uint32_t i;
for(c = 0; c < 0x100; c++){
crc = c<<24;
for(i = 0; i < 8; i++)
/* assumes twos complement */
crc = (crc<<1)^((0-(crc>>31))&POLY);
crctbl[c] = crc;
}
}
uint32_t GenCrc(uint8_t * bfr, size_t size) /* generate crc */
{
uint32_t crc = 0u;
while(size--)
crc = (crc<<8)^crctbl[(crc>>24)^*bfr++];
return(crc);
}
/* carryless multiply modulo crc */
uint32_t MpyModCrc(uint32_t a, uint32_t b) /* (a*b)%crc */
{
uint32_t pd = 0;
uint32_t i;
for(i = 0; i < 32; i++){
/* assumes twos complement */
pd = (pd<<1)^((0-(pd>>31))&POLY);
pd ^= (0-(b>>31))&a;
b <<= 1;
}
return pd;
}
/* exponentiate by repeated squaring modulo crc */
uint32_t PowModCrc(uint32_t p) /* pow(2,p)%crc */
{
uint32_t prd = 0x1u; /* current product */
uint32_t sqr = 0x2u; /* current square */
while(p){
if(p&1)
prd = MpyModCrc(prd, sqr);
sqr = MpyModCrc(sqr, sqr);
p >>= 1;
}
return prd;
}
/* # data bytes */
#define DAT ( 32)
/* # zero bytes */
#define PAD (992)
/* DATA+PAD */
#define CNT (1024)
int main()
{
uint32_t pmc;
uint32_t crc;
uint32_t crf;
uint32_t i;
uint8_t *msg = malloc(CNT);
for(i = 0; i < DAT; i++) /* generate msg */
msg[i] = (uint8_t)rand();
for( ; i < CNT; i++)
msg[i] = 0;
GenTbl(); /* generate crc table */
crc = GenCrc(msg, CNT); /* generate crc normally */
crf = GenCrc(msg, DAT); /* generate crc for data */
pmc = PowModCrc(PAD*8); /* pmc = pow(2,PAD*8)%crc */
crf = MpyModCrc(crf, pmc); /* crf = (crf*pmc)%crc */
printf("%08x %08x\n", crc, crf);
free(msg);
return 0;
}
使用内在函数进行无进位乘法的示例C代码,pclmulqdq == _mm_clmulepi64_si128:
/* crcpadm.c - crc - data has a large number of trailing zeroes */
/* pclmulqdq intrinsic version */
#include <stdio.h>
#include <stdlib.h>
#include <intrin.h>
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
#define POLY (0x104c11db7ull)
#define POLYM ( 0x04c11db7u)
static uint32_t crctbl[256];
static __m128i poly; /* poly */
static __m128i invpoly; /* 2^64 / POLY */
void GenMPoly(void) /* generate __m12i8 poly info */
{
uint64_t N = 0x100000000ull;
uint64_t Q = 0;
for(size_t i = 0; i < 33; i++){
Q <<= 1;
if(N&0x100000000ull){
Q |= 1;
N ^= POLY;
}
N <<= 1;
}
poly.m128i_u64[0] = POLY;
invpoly.m128i_u64[0] = Q;
}
void GenTbl(void) /* generate crc table */
{
uint32_t crc;
uint32_t c;
uint32_t i;
for(c = 0; c < 0x100; c++){
crc = c<<24;
for(i = 0; i < 8; i++)
/* assumes twos complement */
crc = (crc<<1)^((0-(crc>>31))&POLYM);
crctbl[c] = crc;
}
}
uint32_t GenCrc(uint8_t * bfr, size_t size) /* generate crc */
{
uint32_t crc = 0u;
while(size--)
crc = (crc<<8)^crctbl[(crc>>24)^*bfr++];
return(crc);
}
/* carryless multiply modulo crc */
uint32_t MpyModCrc(uint32_t a, uint32_t b) /* (a*b)%crc */
{
__m128i ma, mb, mp, mt;
ma.m128i_u64[0] = a;
mb.m128i_u64[0] = b;
mp = _mm_clmulepi64_si128(ma, mb, 0x00); /* p[0] = a*b */
mt = _mm_clmulepi64_si128(mp, invpoly, 0x00); /* t[1] = (p[0]*((2^64)/POLY))>>64 */
mt = _mm_clmulepi64_si128(mt, poly, 0x01); /* t[0] = t[1]*POLY */
return mp.m128i_u32[0] ^ mt.m128i_u32[0]; /* ret = p[0] ^ t[0] */
}
/* exponentiate by repeated squaring modulo crc */
uint32_t PowModCrc(uint32_t p) /* pow(2,p)%crc */
{
uint32_t prd = 0x1u; /* current product */
uint32_t sqr = 0x2u; /* current square */
while(p){
if(p&1)
prd = MpyModCrc(prd, sqr);
sqr = MpyModCrc(sqr, sqr);
p >>= 1;
}
return prd;
}
/* # data bytes */
#define DAT ( 32)
/* # zero bytes */
#define PAD (992)
/* DATA+PAD */
#define CNT (1024)
int main()
{
uint32_t pmc;
uint32_t crc;
uint32_t crf;
uint32_t i;
uint8_t *msg = malloc(CNT);
GenMPoly(); /* generate __m128 polys */
GenTbl(); /* generate crc table */
for(i = 0; i < DAT; i++) /* generate msg */
msg[i] = (uint8_t)rand();
for( ; i < CNT; i++)
msg[i] = 0;
crc = GenCrc(msg, CNT); /* generate crc normally */
crf = GenCrc(msg, DAT); /* generate crc for data */
pmc = PowModCrc(PAD*8); /* pmc = pow(2,PAD*8)%crc */
crf = MpyModCrc(crf, pmc); /* crf = (crf*pmc)%crc */
printf("%08x %08x\n", crc, crf);
free(msg);
return 0;
}