Editor/Services/ArchHash.cs
using System;
using Sandbox;

namespace Sunless.Architecture;

// FNV-1a 64-bit; order matters — swapped layers are a different build.
public static class ArchHash {
	public const ulong Seed = 14695981039346656037ul;

	const ulong Prime = 1099511628211ul;

	public static ulong Fold( ulong hash, ulong value ) {
		for ( var shift = 0; shift < 64; shift += 8 ) {
			hash = (hash ^ ((value >> shift) & 0xFF)) * Prime;
		}

		return hash;
	}

	public static ulong Fold( ulong hash, int value ) => Fold( hash, (ulong)(uint)value );

	public static ulong Fold( ulong hash, bool value ) => Fold( hash, value ? 1 : 0 );

	// Collapse -0 to +0 so the cache does not split on an invisible sign bit.
	public static ulong Fold( ulong hash, float value ) {
		return Fold( hash, (ulong)(uint)BitConverter.SingleToInt32Bits( value == 0f ? 0f : value ) );
	}

	public static ulong Fold( ulong hash, Vector2 value ) => Fold( Fold( hash, value.x ), value.y );

	public static ulong Fold( ulong hash, Vector3 value ) => Fold( Fold( Fold( hash, value.x ), value.y ), value.z );

	public static ulong Fold( ulong hash, string value ) {
		if ( value is null ) {
			return Fold( hash, -1 );
		}

		hash = Fold( hash, value.Length );

		foreach ( var character in value ) {
			hash = Fold( hash, character );
		}

		return hash;
	}

	public static ulong Of( string value ) => Fold( Seed, value );
}