大多数静态数据流的CRC计算

时间:2014-04-16 23:59:02

标签: checksum crc

背景:

我有一段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)

我知道这个公式不起作用(尝试过),但我希望存在类似的东西。

2 个答案:

答案 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;
}