原理
原理我就不詳細(xì)介紹了暖释,維基百科什么的有很多平项,大概意思就是這么一套操作過后可以極大概率地檢測出數(shù)據(jù)錯誤。我又怎么會告訴你們我也沒有完全看懂。
實現(xiàn)
直接計算法
/**
* CRC32 校驗函數(shù)
* @param pdata 數(shù)據(jù)指針
* @param len 數(shù)據(jù)長度
* @param crc 初值或上段數(shù)據(jù)校驗結(jié)果
* @param poly 多項式
*/
uint32_t CRC32(const uint8_t* pdata, int len, uint32_t crc, uint32_t poly)
{
for (int i = 0; i < len; i++, pdata++)
{
crc ^= ((uint32_t)*pdata << 24);
for (int bits = 0; bits < 8; bits++)
{
if (crc & 0x80000000)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
}
return crc;
}
稍微解釋一下代碼镣煮,大概步驟是:
- 把新的數(shù)據(jù) xor 到之前數(shù)據(jù)的最高 byte
- 判斷最高 bit,如果是 0 就左移鄙麦,是 1 就左移后 xor poly
- 處理完最高 8 個 bits 結(jié)束
網(wǎng)上有些例程是右移處理典唇,其實本質(zhì)是一樣的,只要生成 CRC 的程序和校驗它的程序是一致的就可以胯府。
為了證明我不是隨便找了一段沒驗證過的代碼就貼出來介衔,我這里給出一個 CRC Online Calculator,我貼出的代碼能夠得出和它一致的結(jié)果骂因,各位自己調(diào)試代碼的時候也可以有個參考炎咖。
使用的時候有幾點注意:
- 選擇 CRC-32
- 由于我給出的代碼沒有進行位翻轉(zhuǎn)操作,所以選擇 Custom,然后兩個 reflected 的鉤去掉
- Final Xor Value 填 0
- 剩下的你應(yīng)該懂的
雖然這段代碼簡單明白乘盼,但是網(wǎng)上類似的東西也不少升熊,發(fā)個貼子沒點干貨似乎不太好意思。那么我就多給點料绸栅。
查表法
了解多一些的朋友會知道级野,雖然直接計算的方法代碼特別簡單,但是循環(huán)次數(shù)太多效率不夠高阴幌。根據(jù) xor 運算的結(jié)合律特性勺阐,可以使用查表法提高運行效率卷中。
static uint32_t gsTable[256];
void CRC32_TableInit(uint32_t poly)
{
for(uint32_t i = 0; i < 256; i++)
{
uint32_t crc = i << 24;
for (int bits = 0; bits < 8; bits++)
{
if (crc & 0x80000000)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
gsTable[i] = crc;
}
}
uint32_t CRC32_Table(const uint8_t* pdata, int len, uint32_t crc)
{
uint8_t index;
for (int i = 0; i < len; i++, pdata++)
{
index = *pdata ^ (crc >> 24);
crc = (crc << 8) ^ gsTable[index];
}
return crc;
}
對比上面的代碼矛双,其實就是把 8 bits 的循環(huán)抽了出來。從直接計算的代碼我們看出蟆豫,這八次循環(huán)的計算效果议忽,其實完全取決于新數(shù)據(jù) xor 之后 crc 最高 byte 的值。那么我們?nèi)绻堰@一個 byte 所有可能的值對應(yīng)的計算結(jié)果先存起來十减,就得到了一個 256 數(shù)據(jù)的表栈幸。
使用的時候就拿 xor 之后的最高 byte 去查表,然后把 crc 直接左移 8 個 bits帮辟,再進行一次 xor 就妥了速址。
有些朋友會疑惑,這樣一番操作是不是真的能得到同樣的結(jié)果由驹。我們可以觀察一下表的值芍锚,發(fā)現(xiàn)它的第 0 個值必然是 0,而第 1 個值蔓榄,必然等于 poly并炮。為什么呢?因為如果最高 byte 是 0 的話甥郑,數(shù)據(jù)除了會左移 8 個 bits 以外逃魄,剩下的 bits 都不會改變(xor 0 值也不變)。而最高 byte 如果是 1 的話澜搅,那么它會在第 8 次左移之后 xor 一次 poly伍俘。可以看出至少在這兩種情況的時候勉躺,兩段代碼的計算效果是等價的养篓,而后面的更多種情況就只能自己體會一下了。
查表法是挺不錯赂蕴,但是網(wǎng)上其實也能找得到柳弄。所以呢,就再上一份干貨,這是我實際踩過的坑碧注。
STM32 CRC32
下面的代碼嚣伐,與 STM32 中自帶的 CRC32 模塊的計算效果是等效的。
#define CRC_STM32_POLY 0x04c11db7
#define CRC_STM32_INIT_VALUE 0xFFFFFFFF
uint32_t STM32_CRC32(const uint32_t* pdata, int len, uint32_t crc, uint32_t poly)
{
for (int i = 0; i < len; i++, pdata++)
{
crc ^= *pdata;
for (int bits = 0; bits < 32; bits++)
{
if ((crc & 0x80000000) != 0)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
}
return crc;
}
那么它和我們上面一般的 CRC 運算有什么不同呢萍丐?細(xì)心的朋友可能發(fā)現(xiàn)了轩端,它的計算是 32 bits 一輪的,而由于其是小端存儲逝变,所以其實是從第四個 byte 的最高 bit 開始計算的基茵。和從第一個 byte 開始計算的 CRC 結(jié)果是不一樣的,而且輸入數(shù)據(jù) bytes 個數(shù)必須補零到 4 的整數(shù)倍壳影。
如果你以為它和一開始介紹的算法一樣拱层,就會被它坑到 T^T。而它的算法是內(nèi)置的宴咧,沒得修改根灯,所以只要你想用,就必須按它的來掺栅。
好了烙肺,干貨上完,感謝閱讀氧卧。
Jalon 原創(chuàng)桃笙,轉(zhuǎn)載署名,商用聯(lián)系本人沙绝。