using System; namespace System.IO.Hashing { public static class BinaryPrimitives { public static void WriteUInt32LittleEndian(Span 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 source, Span destination, out int bytesWritten) { if (destination.Length < Size) { bytesWritten = 0; return false; } bytesWritten = StaticHash(source, destination); return true; } private static int StaticHash(ReadOnlySpan source, Span destination) { uint crc = InitialState; crc = Update(crc, source); BinaryPrimitives.WriteUInt32LittleEndian(destination, ~crc); return Size; } private static uint Update(uint crc, ReadOnlySpan source) { for (int i = 0; i < source.Length; i++) { byte idx = (byte)crc; idx ^= source[i]; crc = s_crcLookup[idx] ^ (crc >> 8); } return crc; } } }