using System.Buffers.Binary;
namespace AcDream.Core.Net.Cryptography;
///
/// Simple 32-bit checksum used by the AC packet header. The algorithm is:
///
/// - Seed the accumulator with length << 16.
/// - Sum the input as little-endian uint32s, four bytes at a time.
/// - Fold any tail bytes (length not divisible by 4) into the top of
/// the accumulator with a descending left-shift per byte (24, 16, 8, 0).
///
/// Reimplemented from reading ACE's AGPL reference
/// (references/ACE/Source/ACE.Common/Cryptography/Hash32.cs). See
/// NOTICE.md for the attribution policy.
///
public static class Hash32
{
public static uint Calculate(ReadOnlySpan data)
{
int length = data.Length;
uint checksum = (uint)length << 16;
int wordAligned = length & ~3;
for (int i = 0; i < wordAligned; i += 4)
{
checksum += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(i));
}
// Tail bytes (0, 1, 2, or 3 bytes) with descending shift: first tail
// byte goes into bits 24..31, second into 16..23, third into 8..15.
int shift = 24;
for (int j = wordAligned; j < length; j++)
{
checksum += (uint)data[j] << shift;
shift -= 8;
}
return checksum;
}
}