杂技 CRC深究

数学算法的描述可以坑很多程序员,我也不例外

CRC

CRC32

1.CRC32算法难在网上的源码大多是驱动表计算,无法从源码中推倒实现过程
2.CRC32 正式算法还涉及到数据颠倒和初始化预置值

CRC32参数模型
Name : "CRC-32"
Width : 32
Poly : 04C11DB7
Init : FFFFFFFF
RefIn : True
RefOut : True
XorOut : FFFFFFFF
Check : CBF43926

标准的CRC

CRC的核心算法是 模2除法 [除法过程为XOR]

例:

101101 mod2 1100

101101 000
1100
 11101 000
 1100
   101 000
   110 0
    11 000
    11 00
       000   ->余数 / CRC校检值

例子中的 1100 称为生成项
1.在测试数据后面+[生成项长度-1]位0
2.进行mod2除法
3.余数追加到测试数据后面,形成101101 100作为我们的SIGNED数据

CRC32的生成项长度为33
上面演示的过程是 直接计算法

驱动表法

我们延用上面的例子

首先驱动表法基于交换律

(A XOR B) XOR C = A XOR (B XOR C)

我们的位宽是3 //生成项长度-1 也称为CRC3

原:

101101 000
1100
 11101 000
 1100
   101 000
   110 0
    11 000
    11 00
       000   ->余数 / CRC校检值

我们一共XOR了4次来得到余数
表:

1100
 1100
   1100
    1100
10110100

计算表值->10110100

101101000
10110100
      000

例子中实际上我们的表索引是 [101101]值是[000000]
例子不是很明显,但是我们实际上是用的是消去法
我们使poly XOR (ploy>>n) 直到前约定位与原数据相同[例子中是6]
后三位只需要与ploy XOR的结果 左移6位 再XOR 再查表就可以了

我们的poly表就可以有 111111-000000种变化

在CRC32中,我们的位宽是32位
CRC16 和 CRC32 都是一次处理一个字节的,我们按字节建8位表[总长度255]
8 位表的总长度是 11111111B =255D

我们用标准生成项0x104C11DB7
例子

[0000 0000]->[0x0]
[0000 0001]->[0x04C11DB7]
[0000 0010]->[0x09823B6E]
[0000 0011]->[0x0D4326D9]
[0000 0100]->[0x130476DC]
...

这张表叫做,直接查询表

例:

1111 1111 1111 1000 0000 0000 0000 0000 0000 0000 0000 0
1000 0010 0110 0000 1000 1110 1101 1011 1
 111 1101 1001 1000 1000 1110 1101 1011 10
 100 0001 0011 0000 0100 0111 0110 1101 11
  11 1100 1010 1000 1100 1001 1011 0110 010
  10 0000 1001 1000 0010 0011 1011 0110 111
   1 1100 0011 0000 1110 1010 0000 0000 1010
   1 0000 0100 1100 0001 0001 1101 1011 0111
     1100 0111 1100 1111 1011 1101 1011 1101 0000 0000 0
     1000 0010 0110 0000 1000 1110 1101 1011 1
      100 0101 1010 1111 0011 0011 0110 0110 1000 0000 0
      100 0001 0011 0000 0100 0111 0110 1101 11
           100 1001 1111 0111 0100 0000 1011 0100 0000 0
           100 0001 0011 0000 0100 0111 0110 1101 11
               1000 1100 0111 0000 0111 1101 1001 1100 0
               1000 0010 0110 0000 1000 1110 1101 1011 1
                000 1110 0001 0000 1111 0011 0100 0111 1

1111 1111 1111 1
          1011 0001 1111 0111 0100 0000 1011 0100   //表ff
           100 1001 1111 0111 0100 0000 1011 0100   
                001 0001 0110 0100 1111 1000 0000 0111 1   //表09
                000 1110 0001 0000 1111 0011 0100 0111 1

驱动表法的实现

定义1个32位的register,并将原始数据头放入。
1:register 左移一个字节[剩余长度],从原始数据中读入一个新的字节[剩余长度]
2:利用刚从 register 移出的内容作为下标定位 table 中的一个 32 位的值
3:把这个值 XOR 到 register 中
4:如果还有未处理的数据则回到第一步继续执行

直驱表法

我们算法的本质是
原数据 XOR POLY XOR [POLY>>N]……
驱动表法的本质是
原数据 XOR TABLE[G(POLY)] XOR [TABLE[G(POLY)]>>8N]……

直驱表法例:

1111 1111 1111 1
原数据取出1字节 -> 1111 1111
XOR
寄存器取出1字节 -> 0000 0000
查表[1111 1111]
得 0xB1F740B4 XOR进 寄存器

目前寄存器 1011 0001 1111 0111 0100 0000 1011 0100


原数据取出剩余字节 -> 1111 1
XOR
寄存器取出5位 1011 0
目前寄存器 0011 1110 1110 1000 0001 0110 1000 0000
查表[01001]
得0x22C9F00F XOR进 寄存器
目前寄存器 
          0010 0010 1100 1001 1111 0000 0000 1111
          XOR
          0011 1110 1110 1000 0001 0110 1000 0000
          =
          0001 1100 0010 0001 1110 0110 1000 1111

直驱表法,使原数据成了因子。只用n次的poly 自xor来计算结果。

引入模型

我们之前的计算的确属于CRC。但是与winrar之类的软件求的值并不一样。原因是因为我们没有引入模型。

CRC32参数模型
Name : "CRC-32"
Width : 32
Poly : 04C11DB7
Init : FFFFFFFF
RefIn : True
RefOut : True
XorOut : FFFFFFFF
Check : CBF43926

Init 用于初始化直驱表的寄存器值,我们之前初始化是0
RefIn 需要将待测数据的每个字节颠倒
RefOut 计算结束后 需要将整个寄存器颠倒
XorOut 输出前要用这个值XOR一次寄存器

0011 0001  -> 字符"1"
直驱表法
1.寄存器 FFFFFFFF
2.~0011 0001  -> 1000 1100
3.1000 1100 XOR 1111 1111
4.0111 0011
5.查表[0111 0011] -> 0xEDF73B3E
6.寄存器 0xFFFFFF00 XOR 0xEDF73B3E
7.寄存器 0x1208C43E
8.颠倒寄存器 0111 1100 0010 0011 0001 0000 0100 1000 0x7C231048
9.寄存器 XOR 0xFFFFFFFF -> 0x83DCEFB7
10.TRUE

最后的优化

颠倒 直接查询表 成为 正规查询表
我们肯定不希望,每次颠倒字节的值,毕竟很损耗性能而且实现要多几行函数。[关键是容易忘啊]
所以如果我们把表颠倒好。让我们还按照直驱表法计算。那就方便多了。

有两个颠倒要处理
RefIn和RefOut

乍一想还很麻烦

我们把整个算法几何镜像
RefIn和RefOut就全镜像了
同时表的poly 0x04C11DB7 | 0xEDB88320
表的key[0000 0001] | [1000 0000]
T2[0000 0001] = ~T1[1000 0000] = ~0x690CE0EE = 0x77073096

//直接查询表的生成法
void
CRC32_INIT(){
    int i, j;
    for (i = 0 ; i < 256 ; i++){
        for (j = 0, CRC_TABLE[i] = i<<24 ; j < 8 ; j++){
            //i就是计算因子
            //mem = i<<24 对齐内存
            //mem = (mem<<1)^((mem&0x80000000)?POLYNOMIAL:0)
            //poly = 04C11DB7
            CRC_TABLE[i] = (CRC_TABLE[i]<<1)^((CRC_TABLE[i]&0x80000000)?POLYNOMIAL:0);
        }
    }
}
//所以正规查询表的算法
void
CRC32_INIT(){
    int i, j;
    for (i = 0 ; i < 256 ; i++){
        for (j = 0, CRC_TABLE[i] = i<<24 ; j < 8 ; j++){
            //i就是计算因子
            //mem = i
            //mem = (mem>>1)^((mem&0x1)?~POLYNOMIAL:0)
            //poly = ~poly = ~0x04C11DB7 = 0xEDB88320
            CRC_TABLE[i] = (CRC_TABLE[i]>>1)^((CRC_TABLE[i]&0x1)?POLYNOMIAL:0);
        }
    }
}

我们需要的CRC算法[这里我只考虑了字节对齐的情况]

void
CRC32_INIT(){
    int i, j;
    for (i = 0 ; i < 256 ; i++){
        for (j = 0, CRC_TABLE[i] = i ; j < 8 ; j++){
            CRC_TABLE[i] = (CRC_TABLE[i]>>1)^((CRC_TABLE[i]&1)?POLYNOMIAL:0);
        }
    }
}

unsigned int
CRC32(char *buff, int len){
    //Init模型的处理
    unsigned int reg=0xFFFFFFFF;
    for (int i = 0; i < len; i++){
        reg = (reg >> 8) ^ CRC_TABLE[(reg ^ buff[i]) & 0xff];
    }
    //XorOut的处理
    return ~reg;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容