238 lines
8.0 KiB
C#
238 lines
8.0 KiB
C#
namespace ThingsGateway.NewLife.Security;
|
||
|
||
/// <summary>CRC32校验</summary>
|
||
/// <remarks>
|
||
/// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
|
||
/// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
|
||
///
|
||
/// Polynomials over GF(2) are represented in binary, one bit per coefficient,
|
||
/// with the lowest powers in the most significant bit. Then adding polynomials
|
||
/// is just exclusive-or, and multiplying a polynomial by x is a right shift by
|
||
/// one. If we call the above polynomial p, and represent a byte as the
|
||
/// polynomial q, also with the lowest power in the most significant bit (so the
|
||
/// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
|
||
/// where a mod b means the remainder after dividing a by b.
|
||
///
|
||
/// This calculation is done using the shift-register method of multiplying and
|
||
/// taking the remainder. The register is initialized to zero, and for each
|
||
/// incoming bit, x^32 is added mod p to the register if the bit is a one (where
|
||
/// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
|
||
/// x (which is shifting right by one and adding x^32 mod p if the bit shifted
|
||
/// out is a one). We start with the highest power (least significant bit) of
|
||
/// q and repeat for all eight bits of q.
|
||
///
|
||
/// The table is simply the CRC of all possible eight bit values. This is all
|
||
/// the information needed to generate CRC's on data a byte at a time for all
|
||
/// combinations of CRC register values and incoming bytes.
|
||
/// </remarks>
|
||
public sealed class Crc32 //: HashAlgorithm
|
||
{
|
||
const UInt32 CrcSeed = 0xFFFFFFFF;
|
||
|
||
#region 数据表
|
||
/// <summary>校验表</summary>
|
||
public readonly static UInt32[] Table;
|
||
|
||
static Crc32()
|
||
{
|
||
Table = new UInt32[256];
|
||
const UInt32 kPoly = 0xEDB88320;
|
||
for (UInt32 i = 0; i < 256; i++)
|
||
{
|
||
var r = i;
|
||
for (var j = 0; j < 8; j++)
|
||
if ((r & 1) != 0)
|
||
r = (r >> 1) ^ kPoly;
|
||
else
|
||
r >>= 1;
|
||
Table[i] = r;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
//internal static uint ComputeCrc32(uint oldCrc, byte value)
|
||
//{
|
||
// return (uint)(Table[(oldCrc ^ value) & 0xFF] ^ (oldCrc >> 8));
|
||
//}
|
||
|
||
/// <summary>校验值</summary>
|
||
UInt32 crc = CrcSeed;
|
||
/// <summary>校验值</summary>
|
||
public UInt32 Value { get { return crc ^ CrcSeed; } set { crc = value ^ CrcSeed; } }
|
||
|
||
/// <summary>重置清零</summary>
|
||
public Crc32 Reset() { crc = CrcSeed; return this; }
|
||
|
||
/// <summary>添加整数进行校验</summary>
|
||
/// <param name = "value">
|
||
/// the byte is taken as the lower 8 bits of value
|
||
/// </param>
|
||
public Crc32 Update(Int32 value)
|
||
{
|
||
crc = Table[(crc ^ value) & 0xFF] ^ (crc >> 8);
|
||
|
||
return this;
|
||
}
|
||
|
||
/// <summary>添加字节数组进行校验</summary>
|
||
/// <param name = "buffer">
|
||
/// The buffer which contains the data
|
||
/// </param>
|
||
/// <param name = "offset">
|
||
/// The offset in the buffer where the data starts
|
||
/// </param>
|
||
/// <param name = "count">
|
||
/// The number of data bytes to update the CRC with.
|
||
/// </param>
|
||
public Crc32 Update(Byte[] buffer, Int32 offset = 0, Int32 count = -1)
|
||
{
|
||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||
//if (count < 0) throw new ArgumentOutOfRangeException("count", "Count不能小于0!");
|
||
if (count < 0) count = buffer.Length;
|
||
if (offset < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
|
||
|
||
while (--count >= 0)
|
||
{
|
||
crc = Table[(crc ^ buffer[offset++]) & 0xFF] ^ (crc >> 8);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
|
||
/// <summary>添加数据区进行检验</summary>
|
||
/// <param name="buffer"></param>
|
||
/// <returns></returns>
|
||
public Crc32 Update(ReadOnlySpan<Byte> buffer)
|
||
{
|
||
for (var i = 0; i < buffer.Length; i++)
|
||
{
|
||
crc = Table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
|
||
/// <summary>添加数据流进行校验</summary>
|
||
/// <param name="stream"></param>
|
||
/// <param name="count">数量</param>
|
||
public Crc32 Update(Stream stream, Int64 count = -1)
|
||
{
|
||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||
//if (count < 0) throw new ArgumentOutOfRangeException("count", "Count不能小于0!");
|
||
if (count <= 0) count = Int64.MaxValue;
|
||
|
||
while (--count >= 0)
|
||
{
|
||
var b = stream.ReadByte();
|
||
if (b == -1) break;
|
||
|
||
crc = Table[(crc ^ b) & 0xFF] ^ (crc >> 8);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
|
||
/// <summary>计算校验码</summary>
|
||
/// <param name="buf"></param>
|
||
/// <param name="offset"></param>
|
||
/// <param name="count"></param>
|
||
/// <returns></returns>
|
||
public static UInt32 Compute(Byte[] buf, Int32 offset = 0, Int32 count = -1)
|
||
{
|
||
var crc = new Crc32();
|
||
crc.Update(buf, offset, count);
|
||
return crc.Value;
|
||
}
|
||
|
||
/// <summary>计算校验码</summary>
|
||
/// <param name="buffer"></param>
|
||
/// <returns></returns>
|
||
public static UInt32 Compute(ReadOnlySpan<Byte> buffer)
|
||
{
|
||
var crc = new Crc32();
|
||
crc.Update(buffer);
|
||
return crc.Value;
|
||
}
|
||
|
||
/// <summary>计算数据流校验码</summary>
|
||
/// <param name="stream"></param>
|
||
/// <param name="count"></param>
|
||
/// <returns></returns>
|
||
public static UInt32 Compute(Stream stream, Int32 count = -1)
|
||
{
|
||
var crc = new Crc32();
|
||
crc.Update(stream, count);
|
||
return crc.Value;
|
||
}
|
||
|
||
/// <summary>计算数据流校验码,指定起始位置和字节数偏移量</summary>
|
||
/// <remarks>
|
||
/// 一般用于计算数据包校验码,需要回过头去开始校验,并且可能需要跳过最后的校验码长度。
|
||
/// position小于0时,数据流从当前位置开始计算校验;
|
||
/// position大于等于0时,数据流移到该位置开始计算校验,最后由count决定可能差几个字节不参与计算;
|
||
/// </remarks>
|
||
/// <param name="stream"></param>
|
||
/// <param name="position">如果大于等于0,则表示从该位置开始计算</param>
|
||
/// <param name="count">字节数偏移量,一般用负数表示</param>
|
||
/// <returns></returns>
|
||
public static UInt32 ComputeRange(Stream stream, Int64 position = -1, Int32 count = -1)
|
||
{
|
||
if (position >= 0)
|
||
{
|
||
if (count > 0) count = -count;
|
||
count += (Int32)(stream.Position - position);
|
||
if (count == 0) return 0;
|
||
|
||
stream.Position = position;
|
||
}
|
||
|
||
var crc = new Crc32();
|
||
crc.Update(stream, count);
|
||
return crc.Value;
|
||
}
|
||
/// <summary>
|
||
/// 添加Sequence进行校验
|
||
/// </summary>
|
||
/// <param name="sequence"></param>
|
||
/// <returns></returns>
|
||
public Crc32 Update(ReadOnlySequence<byte> sequence)
|
||
{
|
||
foreach (var segment in sequence)
|
||
{
|
||
Update(segment.Span);
|
||
}
|
||
return this;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算校验码 (Sequence)
|
||
/// </summary>
|
||
/// <param name="sequence"></param>
|
||
/// <returns></returns>
|
||
public static UInt32 Compute(ReadOnlySequence<byte> sequence)
|
||
{
|
||
var crc = new Crc32();
|
||
crc.Update(sequence);
|
||
return crc.Value;
|
||
}
|
||
//#region 抽象实现
|
||
///// <summary>哈希核心</summary>
|
||
///// <param name="array"></param>
|
||
///// <param name="ibStart"></param>
|
||
///// <param name="cbSize"></param>
|
||
//protected override void HashCore(byte[] array, int ibStart, int cbSize)
|
||
//{
|
||
// while (--cbSize >= 0)
|
||
// {
|
||
// crc = Table[(crc ^ array[ibStart++]) & 0xFF] ^ (crc >> 8);
|
||
// }
|
||
//}
|
||
|
||
///// <summary>最后哈希</summary>
|
||
///// <returns></returns>
|
||
//protected override byte[] HashFinal() { return BitConverter.GetBytes(Value); }
|
||
|
||
///// <summary>初始化</summary>
|
||
//public override void Initialize() { }
|
||
//#endregion
|
||
} |