Geometry/StableHash.cs

A struct implementing a stable 64-bit FNV-1a style hash builder. It provides Create(), Add overloads for int, long, bool, float, Vector2, Vector3 and string, and produces a long Value representation of the accumulated hash.

Native Interop
using System;
using Sandbox;

namespace RedSnail.WaterTool2;

/// <summary>
/// 64-bit FNV-1a hash that is stable across sessions, unlike <see cref="HashCode"/> which is seeded per process. Used
/// to stamp every generated piece of water with the inputs it was built from, so only the pieces whose inputs really
/// changed get rebuilt.
/// </summary>
public struct StableHash
{
	private const ulong OffsetBasis = 14695981039346656037UL;
	private const ulong Prime = 1099511628211UL;

	private ulong m_Hash;

	public long Value => unchecked((long)m_Hash);



	public static StableHash Create()
	{
		return new StableHash { m_Hash = OffsetBasis };
	}



	public void Add(int _Value)
	{
		unchecked
		{
			for (int i = 0; i < 4; i++)
			{
				m_Hash ^= (byte)(_Value >> (i * 8));
				m_Hash *= Prime;
			}
		}
	}



	public void Add(long _Value)
	{
		Add(unchecked((int)_Value));
		Add(unchecked((int)(_Value >> 32)));
	}



	public void Add(bool _Value)
	{
		Add(_Value ? 1 : 0);
	}



	public void Add(float _Value)
	{
		Add(BitConverter.SingleToInt32Bits(_Value));
	}



	public void Add(Vector2 _Value)
	{
		Add(_Value.x);
		Add(_Value.y);
	}



	public void Add(Vector3 _Value)
	{
		Add(_Value.x);
		Add(_Value.y);
		Add(_Value.z);
	}



	public void Add(string _Value)
	{
		if (_Value is not null)
		{
			foreach (char c in _Value)
				Add((int)c);
		}

		// Terminator, so "ab" + "c" never collides with "a" + "bc".
		Add(-1);
	}
}