86 lines
2.3 KiB
C#
86 lines
2.3 KiB
C#
using System;
|
|
|
|
namespace System.IO.Hashing
|
|
{
|
|
public static class BinaryPrimitives
|
|
{
|
|
public static void WriteUInt32LittleEndian(Span<byte> destination, uint value)
|
|
{
|
|
if (destination.Length < 4)
|
|
{
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
destination[3] = (byte)(value >> 24);
|
|
destination[2] = (byte)(value >> 16);
|
|
destination[1] = (byte)(value >> 8);
|
|
destination[0] = (byte)value;
|
|
}
|
|
}
|
|
|
|
public sealed partial class Crc32
|
|
{
|
|
private const uint InitialState = 0xFFFF_FFFFu;
|
|
private const int Size = sizeof(uint);
|
|
private static readonly uint[] s_crcLookup = GenerateReflectedTable(0xEDB88320u);
|
|
|
|
private static uint[] GenerateReflectedTable(uint reflectedPolynomial)
|
|
{
|
|
uint[] table = new uint[256];
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
uint val = (uint)i;
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
if ((val & 0b0000_0001) == 0)
|
|
{
|
|
val >>= 1;
|
|
}
|
|
else
|
|
{
|
|
val = (val >> 1) ^ reflectedPolynomial;
|
|
}
|
|
}
|
|
|
|
table[i] = val;
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
|
|
{
|
|
if (destination.Length < Size)
|
|
{
|
|
bytesWritten = 0;
|
|
return false;
|
|
}
|
|
|
|
bytesWritten = StaticHash(source, destination);
|
|
return true;
|
|
}
|
|
|
|
private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination)
|
|
{
|
|
uint crc = InitialState;
|
|
crc = Update(crc, source);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(destination, ~crc);
|
|
return Size;
|
|
}
|
|
|
|
private static uint Update(uint crc, ReadOnlySpan<byte> source)
|
|
{
|
|
for (int i = 0; i < source.Length; i++)
|
|
{
|
|
byte idx = (byte)crc;
|
|
idx ^= source[i];
|
|
crc = s_crcLookup[idx] ^ (crc >> 8);
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
}
|
|
}
|