CRC校验
CRC计算逻辑
原理:将数据除以一个除数(可以表示为多项式,如1001表示为 x^ 3+1,1011表示为x^3+x+1),计算所得的余数作为校验值追加到数据末尾
/*
* 方法1:简单的除法运算
寄存器清零
数据最右边补齐W位0 // W是CRC校验值的位数
when(还有数据){
左移寄存器1位,读取数据的下一位到寄存器的bit 0
if (左移寄存器时出现溢出&值为1){ // 默认当除数溢出,且溢出值与被除数的最高位均为1时(最高位符号位为1),则可以进行减法运算(相当于除了一次,得到了一个余数)
寄存器 ^= poly; //被除数和除数做减法时,需要使用无进位的减法,即XOR运算
}
}
寄存器的值就是校验值了
-------- 这种方法由于按位进行计算,效率较低 --------
*方法2:提前计算出除数在每一次除法运算的值,存入表中,计算时直接查表
0011 0000 // 补W=4个零 (值1)
,,10 011 // poly对齐 (值2)
---------
0001 0110
,,,1 0011 // poly对齐 (值3)
---------
0000 0101 // CRC值 (值4)
值2和值3可以提前算出,因为就相当于对除数提前进行移位运算
CRC值 = 值1 ^ 值2 ^ 值3
= 值1 ^ (值2 ^ 值3)
= 值1 ^ 查表值 // 令 `查表值` = 值2 ^ 值3
*/
public sealed class Crc32 : HashAlgorithm
{
public const uint DefaultPolynomial = 0xEDB88320; // 默认除数多项式,最高位符号位为1,是为了在进行除数运算时,对齐符号位
public const uint DefaultSeed = 0xffffffffu; // 初始化一个足够大的寄存器(通常与CRC的位宽相同),将其填充为全1或全0。此处使用32位CRC
private static uint[] _defaultTable; // 除数的移位表
private readonly uint _seed;
private readonly uint[] _table;
private uint _hash;
public Crc32()
: this(DefaultPolynomial, DefaultSeed)
{
}
public Crc32(uint polynomial, uint seed)
{
_table = InitializeTable(polynomial);
_seed = seed;
_hash = seed;
}
public override void Initialize()
{
_hash = _seed;
}
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
_hash = CalculateHash(_table, _hash, array, ibStart, cbSize);
}
protected override byte[] HashFinal()
{
var hashBuffer = UInt32ToBigEndianBytes(~_hash);
HashValue = hashBuffer;
return hashBuffer;
}
public override int HashSize => 32;
public static uint Compute(byte[] buffer)
{
return Compute(DefaultSeed, buffer);
}
public static uint Compute(uint seed, byte[] buffer)
{
return Compute(DefaultPolynomial, seed, buffer);
}
public static uint Compute(uint polynomial, uint seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
}
/// <summary>
/// 构建多项式移位表,共包括0-255次移位,
/// </summary>
/// <param name="polynomial"></param>
/// <returns></returns>
private static uint[] InitializeTable(uint polynomial)
{
if (polynomial == DefaultPolynomial && _defaultTable != null)
return _defaultTable;
var createTable = new uint[256];
for (var i = 0; i < 256; i++)
{
var entry = (uint)i;
for (var j = 0; j < 8; j++)
if ((entry & 1) == 1)
entry = (entry >> 1) ^ polynomial;
else
entry >>= 1;
createTable[i] = entry;
}
if (polynomial == DefaultPolynomial)
_defaultTable = createTable;
return createTable;
}
private static uint CalculateHash(uint[] table, uint seed, byte[] buffer, int start, int size)
{
var hash = seed;
for (var i = start; i < start + size; i++)
hash = (hash >> 8) ^ table[buffer[i] ^ (hash & 0xff)];
return hash;
}
/// <summary>
/// 小端转为大端
/// </summary>
/// <param name="uint32"></param>
/// <returns></returns>
private static byte[] UInt32ToBigEndianBytes(uint uint32)
{
var result = BitConverter.GetBytes(uint32);
if (BitConverter.IsLittleEndian)
Array.Reverse(result);
return result;
}
}
173

被折叠的 条评论
为什么被折叠?



