stableHash function
Returns the FNV-1a 64-bit hash of canonicalString of value as a
lowercase hex string.
Determinism: on a given platform, equal inputs always yield equal hashes
within a run and across runs. (Across the VM/web boundary the digests differ
— see the library-level platform note.) Map key order does NOT change the
hash (keys are sorted first); list order DOES. null and the string
"null" differ because strings are quoted in the canonical form.
Example:
stableHash(<String, int>{'a': 1, 'b': 2}) ==
stableHash(<String, int>{'b': 2, 'a': 1}); // true
Audited: 2026-06-12 11:26 EDT
Implementation
String stableHash(Object? value) {
final String text = canonicalString(value);
// FNV-1a over UTF-16 code units, held as two 32-bit limbs (hi:lo) so the
// mod-2^64 multiply is computed identically on the VM and the web. XOR the
// datum into the low limb (FNV-1a), then multiply the full 64-bit value by the
// prime via _mulMod64. This reproduces the exact result the native
// `(hash ^ unit) * prime` produced on the VM.
int hi = _fnvOffsetHi;
int lo = _fnvOffsetLo;
for (final int unit in text.codeUnits) {
final int xored = (lo ^ unit) & _mask32;
final (int nextHi, int nextLo) = _mulMod64(hi, xored, _fnvPrimeHi, _fnvPrimeLo);
hi = nextHi;
lo = nextLo;
}
// Both limbs are already unsigned 32-bit, so each renders as a sign-free
// 8-digit hex word; concatenated they are the stable 16-digit digest.
return hi.toRadixString(16).padLeft(8, '0') + lo.toRadixString(16).padLeft(8, '0');
}